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