1 /*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <memory>
17 #include <string>
18
19 #include "animator_option.h"
20 #include "interfaces/napi/kits/utils/napi_utils.h"
21 #include "napi/native_api.h"
22 #include "napi/native_engine/native_value.h"
23 #include "napi/native_node_api.h"
24
25 #include "base/log/ace_trace.h"
26 #include "base/log/log.h"
27 #include "base/memory/ace_type.h"
28 #include "base/memory/referenced.h"
29 #include "bridge/common/utils/utils.h"
30 #include "core/animation/animator.h"
31 #include "core/animation/curve.h"
32 #include "core/animation/curve_animation.h"
33 #include "core/animation/spring_motion.h"
34
35 namespace OHOS::Ace::Napi {
36
37 namespace {
38 constexpr size_t INTERPOLATING_SPRING_PARAMS_SIZE = 4;
39 constexpr char INTERPOLATING_SPRING[] = "interpolating-spring";
40 } // namespace
41
ParseString(napi_env env,napi_value propertyNapi,std::string & property)42 static void ParseString(napi_env env, napi_value propertyNapi, std::string& property)
43 {
44 if (propertyNapi != nullptr) {
45 napi_valuetype valueType = napi_undefined;
46 napi_typeof(env, propertyNapi, &valueType);
47 if (valueType == napi_undefined) {
48 NapiThrow(env, "Required input parameters are missing.", ERROR_CODE_PARAM_INVALID);
49 return;
50 } else if (valueType != napi_string) {
51 NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
52 return;
53 }
54
55 size_t buffSize = 0;
56 napi_status status = napi_get_value_string_utf8(env, propertyNapi, nullptr, 0, &buffSize);
57 if (status != napi_ok || buffSize == 0) {
58 return;
59 }
60 std::unique_ptr<char[]> propertyString = std::make_unique<char[]>(buffSize + 1);
61 size_t retLen = 0;
62 napi_get_value_string_utf8(env, propertyNapi, propertyString.get(), buffSize + 1, &retLen);
63 property = propertyString.get();
64 }
65 }
66
ParseInt(napi_env env,napi_value propertyNapi,int32_t & property)67 static void ParseInt(napi_env env, napi_value propertyNapi, int32_t& property)
68 {
69 if (propertyNapi != nullptr) {
70 napi_valuetype valueType = napi_undefined;
71 napi_typeof(env, propertyNapi, &valueType);
72 if (valueType == napi_undefined) {
73 NapiThrow(env, "Required input parameters are missing.", ERROR_CODE_PARAM_INVALID);
74 return;
75 } else if (valueType != napi_number) {
76 NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
77 return;
78 }
79 napi_get_value_int32(env, propertyNapi, &property);
80 }
81 }
82
ParseDouble(napi_env env,napi_value propertyNapi,double & property)83 static void ParseDouble(napi_env env, napi_value propertyNapi, double& property)
84 {
85 if (propertyNapi != nullptr) {
86 napi_valuetype valueType = napi_undefined;
87 napi_typeof(env, propertyNapi, &valueType);
88 if (valueType == napi_undefined) {
89 NapiThrow(env, "Required input parameters are missing.", ERROR_CODE_PARAM_INVALID);
90 return;
91 } else if (valueType != napi_number) {
92 NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
93 return;
94 }
95 napi_get_value_double(env, propertyNapi, &property);
96 }
97 }
98
StringToFillMode(const std::string & fillMode)99 static FillMode StringToFillMode(const std::string& fillMode)
100 {
101 if (fillMode.compare("forwards") == 0) {
102 return FillMode::FORWARDS;
103 } else if (fillMode.compare("backwards") == 0) {
104 return FillMode::BACKWARDS;
105 } else if (fillMode.compare("both") == 0) {
106 return FillMode::BOTH;
107 } else {
108 return FillMode::NONE;
109 }
110 }
111
StringToAnimationDirection(const std::string & direction)112 static AnimationDirection StringToAnimationDirection(const std::string& direction)
113 {
114 if (direction.compare("alternate") == 0) {
115 return AnimationDirection::ALTERNATE;
116 } else if (direction.compare("reverse") == 0) {
117 return AnimationDirection::REVERSE;
118 } else if (direction.compare("alternate-reverse") == 0) {
119 return AnimationDirection::ALTERNATE_REVERSE;
120 } else {
121 return AnimationDirection::NORMAL;
122 }
123 }
124
ParseOptionToMotion(const std::shared_ptr<AnimatorOption> & option)125 static RefPtr<Motion> ParseOptionToMotion(const std::shared_ptr<AnimatorOption>& option)
126 {
127 const auto& curveStr = option->easing;
128 if (curveStr.back() != ')') {
129 return nullptr;
130 }
131 std::string::size_type leftEmbracePosition = curveStr.find_last_of('(');
132 if (leftEmbracePosition == std::string::npos) {
133 return nullptr;
134 }
135 auto aniTimFuncName = curveStr.substr(0, leftEmbracePosition);
136 if (aniTimFuncName.compare(INTERPOLATING_SPRING)) {
137 return nullptr;
138 }
139 auto params = curveStr.substr(leftEmbracePosition + 1, curveStr.length() - leftEmbracePosition - 2);
140 std::vector<std::string> paramsVector;
141 StringUtils::StringSplitter(params, ',', paramsVector);
142 if (paramsVector.size() != INTERPOLATING_SPRING_PARAMS_SIZE) {
143 return nullptr;
144 }
145 for (auto& param : paramsVector) {
146 Framework::RemoveHeadTailSpace(param);
147 }
148 float velocity = StringUtils::StringToFloat(paramsVector[0]);
149 float mass = StringUtils::StringToFloat(paramsVector[1]);
150 float stiffness = StringUtils::StringToFloat(paramsVector[2]);
151 float damping = StringUtils::StringToFloat(paramsVector[3]);
152 // input velocity is normalized velocity, while the velocity of arkui's springMotion is absolute velocity.
153 velocity = velocity * (option->end - option->begin);
154 if (LessOrEqual(mass, 0)) {
155 mass = 1.0f;
156 }
157 if (LessOrEqual(stiffness, 0)) {
158 stiffness = 1.0f;
159 }
160 if (LessOrEqual(damping, 0)) {
161 damping = 1.0f;
162 }
163 return AceType::MakeRefPtr<SpringMotion>(
164 option->begin, option->end, velocity, AceType::MakeRefPtr<SpringProperty>(mass, stiffness, damping));
165 }
166
ParseAnimatorOption(napi_env env,napi_callback_info info,std::shared_ptr<AnimatorOption> & option)167 static void ParseAnimatorOption(napi_env env, napi_callback_info info, std::shared_ptr<AnimatorOption>& option)
168 {
169 size_t argc = 1;
170 napi_value argv;
171 napi_get_cb_info(env, info, &argc, &argv, NULL, NULL);
172 if (argc != 1) {
173 NapiThrow(env, "The number of parameters must be equal to 1.", ERROR_CODE_PARAM_INVALID);
174 return;
175 }
176 napi_value durationNapi = nullptr;
177 napi_value easingNapi = nullptr;
178 napi_value delayNapi = nullptr;
179 napi_value fillNapi = nullptr;
180 napi_value directionNapi = nullptr;
181 napi_value iterationsNapi = nullptr;
182 napi_value beginNapi = nullptr;
183 napi_value endNapi = nullptr;
184 napi_valuetype valueType = napi_undefined;
185 napi_typeof(env, argv, &valueType);
186 if (valueType == napi_object) {
187 napi_get_named_property(env, argv, "duration", &durationNapi);
188 napi_get_named_property(env, argv, "easing", &easingNapi);
189 napi_get_named_property(env, argv, "delay", &delayNapi);
190 napi_get_named_property(env, argv, "fill", &fillNapi);
191 napi_get_named_property(env, argv, "direction", &directionNapi);
192 napi_get_named_property(env, argv, "iterations", &iterationsNapi);
193 napi_get_named_property(env, argv, "begin", &beginNapi);
194 napi_get_named_property(env, argv, "end", &endNapi);
195 } else {
196 NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
197 return;
198 }
199
200 int32_t duration = 0;
201 int32_t delay = 0;
202 int32_t iterations = 0;
203 double begin = 0.0;
204 double end = 0.0;
205 std::string easing = "ease";
206 std::string fill = "none";
207 std::string direction = "normal";
208 ParseString(env, easingNapi, easing);
209 ParseString(env, fillNapi, fill);
210 ParseString(env, directionNapi, direction);
211 ParseInt(env, durationNapi, duration);
212 ParseInt(env, delayNapi, delay);
213 ParseInt(env, iterationsNapi, iterations);
214 ParseDouble(env, beginNapi, begin);
215 ParseDouble(env, endNapi, end);
216 option->duration = std::max(duration, 0);
217 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
218 option->delay = delay;
219 } else {
220 option->delay = std::max(delay, 0);
221 }
222 option->iterations = iterations >= -1 ? iterations : 1;
223 option->begin = begin;
224 option->end = end;
225 option->easing = easing;
226 option->fill = fill;
227 option->direction = direction;
228 }
229
GetAnimatorResult(napi_env env,napi_callback_info info)230 static AnimatorResult* GetAnimatorResult(napi_env env, napi_callback_info info)
231 {
232 AnimatorResult* animatorResult = nullptr;
233 napi_value thisVar;
234 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
235 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&animatorResult));
236 return animatorResult;
237 }
238
GetAnimatorInResult(napi_env env,napi_callback_info info)239 static RefPtr<Animator> GetAnimatorInResult(napi_env env, napi_callback_info info)
240 {
241 AnimatorResult* animatorResult = GetAnimatorResult(env, info);
242 if (!animatorResult) {
243 return nullptr;
244 }
245 return animatorResult->GetAnimator();
246 }
247
JSReset(napi_env env,napi_callback_info info)248 static napi_value JSReset(napi_env env, napi_callback_info info)
249 {
250 AnimatorResult* animatorResult = nullptr;
251 napi_value thisVar;
252 napi_get_cb_info(env, info, NULL, NULL, &thisVar, NULL);
253 napi_unwrap(env, thisVar, (void**)&animatorResult);
254 if (!animatorResult) {
255 NapiThrow(env, "Internal error. Unwrap animator result is failed.", ERROR_CODE_INTERNAL_ERROR);
256 return nullptr;
257 }
258 auto option = animatorResult->GetAnimatorOption();
259 if (!option) {
260 NapiThrow(env, "Internal error. Option is null in AnimatorResult.", ERROR_CODE_INTERNAL_ERROR);
261 return nullptr;
262 }
263 ParseAnimatorOption(env, info, option);
264 auto animator = animatorResult->GetAnimator();
265 if (!animator) {
266 NapiThrow(env, "Internal error. Animator is null in AnimatorResult.", ERROR_CODE_INTERNAL_ERROR);
267 return nullptr;
268 }
269 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator reset, id:%{public}d", animator->GetId());
270 animator->ClearInterpolators();
271 animator->ResetIsReverse();
272 animatorResult->ApplyOption();
273 napi_ref onframeRef = animatorResult->GetOnframeRef();
274 if (onframeRef) {
275 auto onFrameCallback = [env, onframeRef, id = animator->GetId(),
276 weakOption = std::weak_ptr<AnimatorOption>(animatorResult->GetAnimatorOption())](
277 double value) {
278 napi_handle_scope scope = nullptr;
279 napi_open_handle_scope(env, &scope);
280 if (scope == nullptr) {
281 TAG_LOGW(
282 AceLogTag::ACE_ANIMATION, "ohos.animator call onFrame failed, scope is null, id:%{public}d", id);
283 return;
284 }
285 napi_value ret = nullptr;
286 napi_value valueNapi = nullptr;
287 napi_value onframe = nullptr;
288 auto result = napi_get_reference_value(env, onframeRef, &onframe);
289 auto option = weakOption.lock();
290 if (!(result == napi_ok && onframe && option)) {
291 TAG_LOGW(AceLogTag::ACE_ANIMATION,
292 "ohos.animator call onFrame failed, get reference result:%{public}d, id:%{public}d",
293 result == napi_ok, id);
294 napi_close_handle_scope(env, scope);
295 return;
296 }
297 ACE_SCOPED_TRACE(
298 "ohos.animator onframe. duration:%d, curve:%s, id:%d", option->duration, option->easing.c_str(), id);
299 napi_create_double(env, value, &valueNapi);
300 napi_call_function(env, nullptr, onframe, 1, &valueNapi, &ret);
301 napi_close_handle_scope(env, scope);
302 };
303 RefPtr<Animation<double>> animation;
304 RefPtr<Motion> motion = ParseOptionToMotion(option);
305 if (motion) {
306 motion->AddListener(onFrameCallback);
307 animatorResult->SetMotion(motion);
308 } else {
309 auto curve = Framework::CreateCurve(option->easing);
310 animation = AceType::MakeRefPtr<CurveAnimation<double>>(option->begin, option->end, curve);
311 animation->AddListener(onFrameCallback);
312 animator->AddInterpolator(animation);
313 animatorResult->SetMotion(nullptr);
314 }
315 }
316 napi_value result;
317 napi_get_null(env, &result);
318 return result;
319 }
320
321 // since API 9 deprecated
JSUpdate(napi_env env,napi_callback_info info)322 static napi_value JSUpdate(napi_env env, napi_callback_info info)
323 {
324 return JSReset(env, info);
325 }
326
JSPlay(napi_env env,napi_callback_info info)327 static napi_value JSPlay(napi_env env, napi_callback_info info)
328 {
329 auto animatorResult = GetAnimatorResult(env, info);
330 if (!animatorResult) {
331 TAG_LOGW(AceLogTag::ACE_ANIMATION, "ohos.animator: cannot find animator result when call play");
332 return nullptr;
333 }
334 auto animator = animatorResult->GetAnimator();
335 if (!animator) {
336 TAG_LOGW(AceLogTag::ACE_ANIMATION, "ohos.animator: no animator is created when call play");
337 return nullptr;
338 }
339 if (!animator->HasScheduler()) {
340 auto result = animator->AttachSchedulerOnContainer();
341 if (!result) {
342 TAG_LOGW(AceLogTag::ACE_ANIMATION,
343 "ohos.animator: play failed, animator is not bound to specific context, use uiContext.createAnimator "
344 "instead, id:%{public}d",
345 animator->GetId());
346 return nullptr;
347 }
348 }
349 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator play, id:%{public}d, %{public}s",
350 animator->GetId(), animatorResult->GetAnimatorOption()->ToString().c_str());
351 if (animatorResult->GetMotion()) {
352 animator->PlayMotion(animatorResult->GetMotion());
353 } else {
354 animator->Play();
355 }
356 animator->PrintVsyncInfoIfNeed();
357 napi_value result = nullptr;
358 napi_get_null(env, &result);
359 return result;
360 }
361
JSFinish(napi_env env,napi_callback_info info)362 static napi_value JSFinish(napi_env env, napi_callback_info info)
363 {
364 auto animator = GetAnimatorInResult(env, info);
365 if (!animator) {
366 return nullptr;
367 }
368 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator finish, id:%{public}d", animator->GetId());
369 animator->Finish();
370 napi_value result = nullptr;
371 napi_get_null(env, &result);
372 return result;
373 }
374
JSPause(napi_env env,napi_callback_info info)375 static napi_value JSPause(napi_env env, napi_callback_info info)
376 {
377 auto animator = GetAnimatorInResult(env, info);
378 if (!animator) {
379 return nullptr;
380 }
381 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator pause, id:%{public}d", animator->GetId());
382 animator->Pause();
383 napi_value result;
384 napi_get_null(env, &result);
385 return result;
386 }
387
JSCancel(napi_env env,napi_callback_info info)388 static napi_value JSCancel(napi_env env, napi_callback_info info)
389 {
390 auto animator = GetAnimatorInResult(env, info);
391 if (!animator) {
392 return nullptr;
393 }
394 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator cancel, id:%{public}d", animator->GetId());
395 animator->Cancel();
396 napi_value result;
397 napi_get_null(env, &result);
398 return result;
399 }
400
JSReverse(napi_env env,napi_callback_info info)401 static napi_value JSReverse(napi_env env, napi_callback_info info)
402 {
403 auto animatorResult = GetAnimatorResult(env, info);
404 if (!animatorResult) {
405 TAG_LOGW(AceLogTag::ACE_ANIMATION, "ohos.animator: cannot find animator result when call reverse");
406 return nullptr;
407 }
408 if (animatorResult->GetMotion()) {
409 TAG_LOGW(AceLogTag::ACE_ANIMATION, "ohos.animator: interpolatingSpringCurve, cannot reverse");
410 return nullptr;
411 }
412 auto animator = animatorResult->GetAnimator();
413 if (!animator) {
414 TAG_LOGW(AceLogTag::ACE_ANIMATION, "ohos.animator: no animator is created when call reverse");
415 return nullptr;
416 }
417 if (!animator->HasScheduler()) {
418 auto result = animator->AttachSchedulerOnContainer();
419 if (!result) {
420 TAG_LOGW(AceLogTag::ACE_ANIMATION,
421 "ohos.animator: reverse failed, animator is not bound to specific context, use "
422 "uiContext.createAnimator instead, id:%{public}d",
423 animator->GetId());
424 return nullptr;
425 }
426 }
427 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator reverse, id:%{public}d", animator->GetId());
428 animator->Reverse();
429 napi_value result;
430 napi_get_null(env, &result);
431 return result;
432 }
433
ParseJsValue(napi_env env,napi_value jsObject,const std::string & name,int32_t & data)434 static bool ParseJsValue(napi_env env, napi_value jsObject, const std::string& name, int32_t& data)
435 {
436 napi_value value = nullptr;
437 napi_get_named_property(env, jsObject, name.c_str(), &value);
438 napi_valuetype type = napi_undefined;
439 napi_typeof(env, value, &type);
440 if (type == napi_number) {
441 napi_get_value_int32(env, value, &data);
442 return true;
443 } else {
444 return false;
445 }
446 return true;
447 }
448
ParseExpectedFrameRateRange(napi_env env,napi_callback_info info,FrameRateRange & frameRateRange)449 static napi_value ParseExpectedFrameRateRange(napi_env env, napi_callback_info info, FrameRateRange& frameRateRange)
450 {
451 size_t argc = 1;
452 napi_value argv[1];
453 napi_value thisVar = nullptr;
454 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
455 if (argc != 1) {
456 NapiThrow(env, "The number of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
457 return nullptr;
458 }
459
460 napi_value& nativeObj = argv[0];
461 if (nativeObj == nullptr) {
462 NapiThrow(env, "The nativeObj is nullptr.", ERROR_CODE_PARAM_INVALID);
463 return nullptr;
464 }
465
466 int32_t minFPS = 0;
467 int32_t maxFPS = 0;
468 int32_t expectedFPS = 0;
469 ParseJsValue(env, nativeObj, "min", minFPS);
470 ParseJsValue(env, nativeObj, "max", maxFPS);
471 ParseJsValue(env, nativeObj, "expected", expectedFPS);
472
473 frameRateRange.Set(minFPS, maxFPS, expectedFPS);
474 if (!frameRateRange.IsValid()) {
475 NapiThrow(env, "ExpectedFrameRateRange Error", ERROR_CODE_PARAM_INVALID);
476 return nullptr;
477 }
478 return nullptr;
479 }
480
JSSetExpectedFrameRateRange(napi_env env,napi_callback_info info)481 static napi_value JSSetExpectedFrameRateRange(napi_env env, napi_callback_info info)
482 {
483 auto animatorResult = GetAnimatorResult(env, info);
484 if (!animatorResult) {
485 TAG_LOGW(AceLogTag::ACE_ANIMATION, "ohos.animator: cannot find animator when call SetExpectedFrameRateRange");
486 return nullptr;
487 }
488 auto animator = animatorResult->GetAnimator();
489 if (!animator) {
490 TAG_LOGW(AceLogTag::ACE_ANIMATION, "ohos.animator: no animator is created when call SetExpectedFrameRateRange");
491 return nullptr;
492 }
493 if (!animator->HasScheduler()) {
494 auto result = animator->AttachSchedulerOnContainer();
495 if (!result) {
496 TAG_LOGW(AceLogTag::ACE_ANIMATION,
497 "ohos.animator: SetExpectedFrameRateRange failed because animator is not bound to specific context, "
498 "use uiContext.createAnimator instead, id:%{public}d",
499 animator->GetId());
500 return nullptr;
501 }
502 }
503 FrameRateRange frameRateRange;
504 ParseExpectedFrameRateRange(env, info, frameRateRange);
505 animator->SetExpectedFrameRateRange(frameRateRange);
506 TAG_LOGI(AceLogTag::ACE_ANIMATION, "animator id:%{public}d SetExpectedFrameRateRange"
507 "{%{public}d, %{public}d, %{public}d}", animator->GetId(), frameRateRange.min_, frameRateRange.max_,
508 frameRateRange.preferred_);
509 return nullptr;
510 }
511
SetOnframe(napi_env env,napi_callback_info info)512 static napi_value SetOnframe(napi_env env, napi_callback_info info)
513 {
514 AnimatorResult* animatorResult = nullptr;
515 size_t argc = 1;
516 napi_value thisVar = nullptr;
517 napi_value onframe = nullptr;
518 napi_get_cb_info(env, info, &argc, &onframe, &thisVar, NULL);
519 napi_unwrap(env, thisVar, (void**)&animatorResult);
520 if (!animatorResult) {
521 return nullptr;
522 }
523 auto option = animatorResult->GetAnimatorOption();
524 if (!option) {
525 return nullptr;
526 }
527 auto animator = animatorResult->GetAnimator();
528 if (!animator) {
529 return nullptr;
530 }
531 animator->ClearInterpolators();
532 // convert onframe function to reference
533 napi_ref onframeRef = animatorResult->GetOnframeRef();
534 if (onframeRef) {
535 uint32_t count = 0;
536 napi_reference_unref(env, onframeRef, &count);
537 }
538 napi_create_reference(env, onframe, 1, &onframeRef);
539 animatorResult->SetOnframeRef(onframeRef);
540 auto onFrameCallback = [env, onframeRef, id = animator->GetId(),
541 weakOption = std::weak_ptr<AnimatorOption>(animatorResult->GetAnimatorOption())](
542 double value) {
543 napi_handle_scope scope = nullptr;
544 napi_open_handle_scope(env, &scope);
545 if (scope == nullptr) {
546 TAG_LOGW(AceLogTag::ACE_ANIMATION, "ohos.animator call onFrame failed, scope is null, id:%{public}d", id);
547 return;
548 }
549 napi_value ret = nullptr;
550 napi_value valueNapi = nullptr;
551 napi_value onframe = nullptr;
552 auto result = napi_get_reference_value(env, onframeRef, &onframe);
553 auto option = weakOption.lock();
554 if (!(result == napi_ok && onframe && option)) {
555 TAG_LOGW(AceLogTag::ACE_ANIMATION,
556 "ohos.animator call onFrame failed, get reference result:%{public}d, id:%{public}d", result == napi_ok,
557 id);
558 napi_close_handle_scope(env, scope);
559 return;
560 }
561 ACE_SCOPED_TRACE(
562 "ohos.animator onframe. duration:%d, curve:%s, id:%d", option->duration, option->easing.c_str(), id);
563 napi_create_double(env, value, &valueNapi);
564 napi_call_function(env, nullptr, onframe, 1, &valueNapi, &ret);
565 napi_close_handle_scope(env, scope);
566 };
567 RefPtr<Animation<double>> animation;
568 RefPtr<Motion> motion = ParseOptionToMotion(option);
569 if (motion) {
570 motion->AddListener(onFrameCallback);
571 animatorResult->SetMotion(motion);
572 } else {
573 auto curve = Framework::CreateCurve(option->easing);
574 animation = AceType::MakeRefPtr<CurveAnimation<double>>(option->begin, option->end, curve);
575 animation->AddListener(onFrameCallback);
576 animator->AddInterpolator(animation);
577 animatorResult->SetMotion(nullptr);
578 }
579 if (!animator->HasScheduler()) {
580 animator->AttachSchedulerOnContainer();
581 }
582 napi_value undefined;
583 napi_get_undefined(env, &undefined);
584 return undefined;
585 }
586
SetOnFrame(napi_env env,napi_callback_info info)587 static napi_value SetOnFrame(napi_env env, napi_callback_info info)
588 {
589 return SetOnframe(env, info);
590 }
591
SetOnfinish(napi_env env,napi_callback_info info)592 static napi_value SetOnfinish(napi_env env, napi_callback_info info)
593 {
594 AnimatorResult* animatorResult = nullptr;
595 size_t argc = 1;
596 napi_value thisVar = nullptr;
597 napi_value onfinish = nullptr;
598 napi_get_cb_info(env, info, &argc, &onfinish, &thisVar, NULL);
599 napi_unwrap(env, thisVar, (void**)&animatorResult);
600 if (!animatorResult) {
601 return nullptr;
602 }
603 auto option = animatorResult->GetAnimatorOption();
604 if (!option) {
605 return nullptr;
606 }
607 auto animator = animatorResult->GetAnimator();
608 if (!animator) {
609 return nullptr;
610 }
611 // convert onfinish function to reference
612 napi_ref onfinishRef = animatorResult->GetOnfinishRef();
613 if (onfinishRef) {
614 uint32_t count = 0;
615 napi_reference_unref(env, onfinishRef, &count);
616 }
617 napi_create_reference(env, onfinish, 1, &onfinishRef);
618 animatorResult->SetOnfinishRef(onfinishRef);
619 animator->ClearStopListeners();
620 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator set finish callback, id:%{public}d", animator->GetId());
621 animator->AddStopListener([env, onfinishRef, id = animator->GetId()] {
622 napi_handle_scope scope = nullptr;
623 napi_open_handle_scope(env, &scope);
624 if (scope == nullptr) {
625 TAG_LOGW(AceLogTag::ACE_ANIMATION,
626 "ohos.animator call finish callback failed, scope is null, id:%{public}d", id);
627 return;
628 }
629 napi_value ret = nullptr;
630 napi_value onfinish = nullptr;
631 auto result = napi_get_reference_value(env, onfinishRef, &onfinish);
632 if (result != napi_ok || onfinish == nullptr) {
633 napi_close_handle_scope(env, scope);
634 TAG_LOGW(AceLogTag::ACE_ANIMATION,
635 "ohos.animator call finish callback failed, get onFinish failed, id:%{public}d", id);
636 return;
637 }
638 ACE_SCOPED_TRACE("ohos.animator finishCallback, id:%d", id);
639 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator call finish callback, id:%{public}d", id);
640 result = napi_call_function(env, NULL, onfinish, 0, NULL, &ret);
641 if (result != napi_ok) {
642 TAG_LOGW(
643 AceLogTag::ACE_ANIMATION, "ohos.animator call finish callback failed, napi error, id:%{public}d", id);
644 }
645 napi_close_handle_scope(env, scope);
646 });
647 napi_value undefined;
648 napi_get_undefined(env, &undefined);
649 return undefined;
650 }
651
SetOnFinish(napi_env env,napi_callback_info info)652 static napi_value SetOnFinish(napi_env env, napi_callback_info info)
653 {
654 return SetOnfinish(env, info);
655 }
656
SetOncancel(napi_env env,napi_callback_info info)657 static napi_value SetOncancel(napi_env env, napi_callback_info info)
658 {
659 AnimatorResult* animatorResult = nullptr;
660 size_t argc = 1;
661 napi_value thisVar = nullptr;
662 napi_value oncancel = nullptr;
663 napi_get_cb_info(env, info, &argc, &oncancel, &thisVar, NULL);
664 napi_unwrap(env, thisVar, (void**)&animatorResult);
665 if (!animatorResult) {
666 return nullptr;
667 }
668 auto option = animatorResult->GetAnimatorOption();
669 if (!option) {
670 return nullptr;
671 }
672 auto animator = animatorResult->GetAnimator();
673 if (!animator) {
674 return nullptr;
675 }
676 // convert oncancel function to reference
677 napi_ref oncancelRef = animatorResult->GetOncancelRef();
678 if (oncancelRef) {
679 uint32_t count = 0;
680 napi_reference_unref(env, oncancelRef, &count);
681 }
682 napi_create_reference(env, oncancel, 1, &oncancelRef);
683 animatorResult->SetOncancelRef(oncancelRef);
684 animator->ClearIdleListeners();
685 animator->AddIdleListener([env, oncancelRef] {
686 napi_handle_scope scope = nullptr;
687 napi_open_handle_scope(env, &scope);
688 if (scope == nullptr) {
689 return;
690 }
691 napi_value ret = nullptr;
692 napi_value oncancel = nullptr;
693 auto result = napi_get_reference_value(env, oncancelRef, &oncancel);
694 if (result != napi_ok || oncancel == nullptr) {
695 napi_close_handle_scope(env, scope);
696 return;
697 }
698 napi_call_function(env, NULL, oncancel, 0, NULL, &ret);
699 napi_close_handle_scope(env, scope);
700 });
701 napi_value undefined;
702 napi_get_undefined(env, &undefined);
703 return undefined;
704 }
705
SetOnCancel(napi_env env,napi_callback_info info)706 static napi_value SetOnCancel(napi_env env, napi_callback_info info)
707 {
708 return SetOncancel(env, info);
709 }
710
SetOnrepeat(napi_env env,napi_callback_info info)711 static napi_value SetOnrepeat(napi_env env, napi_callback_info info)
712 {
713 AnimatorResult* animatorResult = nullptr;
714 size_t argc = 1;
715 napi_value thisVar = nullptr;
716 napi_value onrepeat = nullptr;
717 napi_get_cb_info(env, info, &argc, &onrepeat, &thisVar, NULL);
718 napi_unwrap(env, thisVar, (void**)&animatorResult);
719 if (!animatorResult) {
720 return nullptr;
721 }
722 auto option = animatorResult->GetAnimatorOption();
723 if (!option) {
724 return nullptr;
725 }
726 auto animator = animatorResult->GetAnimator();
727 if (!animator) {
728 return nullptr;
729 }
730 // convert onrepeat function to reference
731 napi_ref onrepeatRef = animatorResult->GetOnrepeatRef();
732 if (onrepeatRef) {
733 uint32_t count = 0;
734 napi_reference_unref(env, onrepeatRef, &count);
735 }
736 napi_create_reference(env, onrepeat, 1, &onrepeatRef);
737 animatorResult->SetOnrepeatRef(onrepeatRef);
738 animator->ClearRepeatListeners();
739 animator->AddRepeatListener([env, onrepeatRef] {
740 napi_handle_scope scope = nullptr;
741 napi_open_handle_scope(env, &scope);
742 if (scope == nullptr) {
743 return;
744 }
745 napi_value ret = nullptr;
746 napi_value onrepeat = nullptr;
747 auto result = napi_get_reference_value(env, onrepeatRef, &onrepeat);
748 if (result != napi_ok || onrepeat == nullptr) {
749 napi_close_handle_scope(env, scope);
750 return;
751 }
752 napi_call_function(env, NULL, onrepeat, 0, NULL, &ret);
753 napi_close_handle_scope(env, scope);
754 });
755 napi_value undefined;
756 napi_get_undefined(env, &undefined);
757 return undefined;
758 }
759
SetOnRepeat(napi_env env,napi_callback_info info)760 static napi_value SetOnRepeat(napi_env env, napi_callback_info info)
761 {
762 return SetOnrepeat(env, info);
763 }
764
JSCreate(napi_env env,napi_callback_info info)765 static napi_value JSCreate(napi_env env, napi_callback_info info)
766 {
767 auto option = std::make_shared<AnimatorOption>();
768 ParseAnimatorOption(env, info, option);
769 auto animator = CREATE_ANIMATOR("ohos.animator");
770 animator->AttachSchedulerOnContainer();
771 AnimatorResult* animatorResult = new AnimatorResult(animator, option);
772 napi_value jsAnimator = nullptr;
773 napi_create_object(env, &jsAnimator);
774 napi_wrap(
775 env, jsAnimator, animatorResult,
776 [](napi_env env, void* data, void* hint) {
777 AnimatorResult* animatorResult = (AnimatorResult*)data;
778 // release four references(onFunc) before releasing animatorResult
779 animatorResult->Destroy(env);
780 delete animatorResult;
781 },
782 nullptr, nullptr);
783 napi_property_descriptor resultFuncs[] = {
784 DECLARE_NAPI_FUNCTION("update", JSUpdate),
785 DECLARE_NAPI_FUNCTION("reset", JSReset),
786 DECLARE_NAPI_FUNCTION("play", JSPlay),
787 DECLARE_NAPI_FUNCTION("finish", JSFinish),
788 DECLARE_NAPI_FUNCTION("pause", JSPause),
789 DECLARE_NAPI_FUNCTION("cancel", JSCancel),
790 DECLARE_NAPI_FUNCTION("reverse", JSReverse),
791 DECLARE_NAPI_FUNCTION("setExpectedFrameRateRange", JSSetExpectedFrameRateRange),
792 DECLARE_NAPI_SETTER("onframe", SetOnframe),
793 DECLARE_NAPI_SETTER("onfinish", SetOnfinish),
794 DECLARE_NAPI_SETTER("oncancel", SetOncancel),
795 DECLARE_NAPI_SETTER("onrepeat", SetOnrepeat),
796 DECLARE_NAPI_SETTER("onFrame", SetOnFrame),
797 DECLARE_NAPI_SETTER("onFinish", SetOnFinish),
798 DECLARE_NAPI_SETTER("onCancel", SetOnCancel),
799 DECLARE_NAPI_SETTER("onRepeat", SetOnRepeat),
800 };
801
802 NAPI_CALL(env, napi_define_properties(env, jsAnimator, sizeof(resultFuncs) / sizeof(resultFuncs[0]), resultFuncs));
803 return jsAnimator;
804 }
805
806 // since API 9 deprecated
JSCreateAnimator(napi_env env,napi_callback_info info)807 static napi_value JSCreateAnimator(napi_env env, napi_callback_info info)
808 {
809 return JSCreate(env, info);
810 }
811
AnimatorExport(napi_env env,napi_value exports)812 static napi_value AnimatorExport(napi_env env, napi_value exports)
813 {
814 napi_property_descriptor animatorDesc[] = {
815 DECLARE_NAPI_FUNCTION("create", JSCreate),
816 DECLARE_NAPI_FUNCTION("createAnimator", JSCreateAnimator),
817 };
818 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(animatorDesc) / sizeof(animatorDesc[0]), animatorDesc));
819 return exports;
820 }
821
822 static napi_module animatorModule = {
823 .nm_version = 1,
824 .nm_flags = 0,
825 .nm_filename = nullptr,
826 .nm_register_func = AnimatorExport,
827 .nm_modname = "animator",
828 .nm_priv = ((void*)0),
829 .reserved = { 0 },
830 };
831
AnimatorRegister()832 extern "C" __attribute__((constructor)) void AnimatorRegister()
833 {
834 napi_module_register(&animatorModule);
835 }
836
ApplyOption()837 void AnimatorResult::ApplyOption()
838 {
839 CHECK_NULL_VOID(animator_);
840 CHECK_NULL_VOID(option_);
841 if (motion_) {
842 // duration not works. Iteration can only be 1. Direction can only be normal.
843 animator_->SetIteration(1);
844 animator_->SetAnimationDirection(AnimationDirection::NORMAL);
845 } else {
846 animator_->SetDuration(option_->duration);
847 animator_->SetIteration(option_->iterations);
848 animator_->SetAnimationDirection(StringToAnimationDirection(option_->direction));
849 }
850 animator_->SetStartDelay(option_->delay);
851 // FillMode not works for motion in animator implementation.
852 animator_->SetFillMode(StringToFillMode(option_->fill));
853 }
854
Destroy(napi_env env)855 void AnimatorResult::Destroy(napi_env env)
856 {
857 if (animator_) {
858 if (!animator_->IsStopped()) {
859 animator_->Stop();
860 TAG_LOGI(AceLogTag::ACE_ANIMATION, "ohos.animator force stopping done when destroying, id:%{public}d",
861 animator_->GetId());
862 }
863 }
864 if (onframe_ != nullptr) {
865 napi_delete_reference(env, onframe_);
866 }
867 if (onfinish_ != nullptr) {
868 napi_delete_reference(env, onfinish_);
869 }
870 if (oncancel_ != nullptr) {
871 napi_delete_reference(env, oncancel_);
872 }
873 if (onrepeat_ != nullptr) {
874 napi_delete_reference(env, onrepeat_);
875 }
876 }
877
878 } // namespace OHOS::Ace::Napi
879