• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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 "base/thread/frame_trace_adapter.h"
30 #include "bridge/common/utils/utils.h"
31 #include "core/animation/animator.h"
32 #include "core/animation/curve.h"
33 #include "core/animation/curve_animation.h"
34 #include "core/animation/spring_motion.h"
35 
36 namespace OHOS::Ace::Napi {
37 
38 namespace {
39 constexpr size_t INTERPOLATING_SPRING_PARAMS_SIZE = 4;
40 constexpr char INTERPOLATING_SPRING[] = "interpolating-spring";
41 } // namespace
42 
ParseString(napi_env env,napi_value propertyNapi,std::string & property)43 static void ParseString(napi_env env, napi_value propertyNapi, std::string& property)
44 {
45     if (propertyNapi != nullptr) {
46         napi_valuetype valueType = napi_undefined;
47         napi_typeof(env, propertyNapi, &valueType);
48         if (valueType == napi_undefined) {
49             NapiThrow(env, "Required input parameters are missing.", ERROR_CODE_PARAM_INVALID);
50             return;
51         } else if (valueType != napi_string) {
52             NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
53             return;
54         }
55 
56         size_t buffSize = 0;
57         napi_status status = napi_get_value_string_utf8(env, propertyNapi, nullptr, 0, &buffSize);
58         if (status != napi_ok || buffSize == 0) {
59             return;
60         }
61         std::unique_ptr<char[]> propertyString = std::make_unique<char[]>(buffSize + 1);
62         size_t retLen = 0;
63         napi_get_value_string_utf8(env, propertyNapi, propertyString.get(), buffSize + 1, &retLen);
64         property = propertyString.get();
65     }
66 }
67 
ParseInt(napi_env env,napi_value propertyNapi,int32_t & property)68 static void ParseInt(napi_env env, napi_value propertyNapi, int32_t& property)
69 {
70     if (propertyNapi != nullptr) {
71         napi_valuetype valueType = napi_undefined;
72         napi_typeof(env, propertyNapi, &valueType);
73         if (valueType == napi_undefined) {
74             NapiThrow(env, "Required input parameters are missing.", ERROR_CODE_PARAM_INVALID);
75             return;
76         } else if (valueType != napi_number) {
77             NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
78             return;
79         }
80         napi_get_value_int32(env, propertyNapi, &property);
81     }
82 }
83 
ParseDouble(napi_env env,napi_value propertyNapi,double & property)84 static void ParseDouble(napi_env env, napi_value propertyNapi, double& property)
85 {
86     if (propertyNapi != nullptr) {
87         napi_valuetype valueType = napi_undefined;
88         napi_typeof(env, propertyNapi, &valueType);
89         if (valueType == napi_undefined) {
90             NapiThrow(env, "Required input parameters are missing.", ERROR_CODE_PARAM_INVALID);
91             return;
92         } else if (valueType != napi_number) {
93             NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
94             return;
95         }
96         napi_get_value_double(env, propertyNapi, &property);
97     }
98 }
99 
StringToFillMode(const std::string & fillMode)100 static FillMode StringToFillMode(const std::string& fillMode)
101 {
102     if (fillMode.compare("forwards") == 0) {
103         return FillMode::FORWARDS;
104     } else if (fillMode.compare("backwards") == 0) {
105         return FillMode::BACKWARDS;
106     } else if (fillMode.compare("both") == 0) {
107         return FillMode::BOTH;
108     } else {
109         return FillMode::NONE;
110     }
111 }
112 
StringToAnimationDirection(const std::string & direction)113 static AnimationDirection StringToAnimationDirection(const std::string& direction)
114 {
115     if (direction.compare("alternate") == 0) {
116         return AnimationDirection::ALTERNATE;
117     } else if (direction.compare("reverse") == 0) {
118         return AnimationDirection::REVERSE;
119     } else if (direction.compare("alternate-reverse") == 0) {
120         return AnimationDirection::ALTERNATE_REVERSE;
121     } else {
122         return AnimationDirection::NORMAL;
123     }
124 }
125 
ParseOptionToMotion(const std::shared_ptr<AnimatorOption> & option)126 static RefPtr<Motion> ParseOptionToMotion(const std::shared_ptr<AnimatorOption>& option)
127 {
128     const auto& curveStr = option->easing;
129     if (curveStr.back() != ')') {
130         return nullptr;
131     }
132     std::string::size_type leftEmbracePosition = curveStr.find_last_of('(');
133     if (leftEmbracePosition == std::string::npos) {
134         return nullptr;
135     }
136     auto aniTimFuncName = curveStr.substr(0, leftEmbracePosition);
137     if (aniTimFuncName.compare(INTERPOLATING_SPRING)) {
138         return nullptr;
139     }
140     auto params = curveStr.substr(leftEmbracePosition + 1, curveStr.length() - leftEmbracePosition - 2);
141     std::vector<std::string> paramsVector;
142     StringUtils::StringSplitter(params, ',', paramsVector);
143     if (paramsVector.size() != INTERPOLATING_SPRING_PARAMS_SIZE) {
144         return nullptr;
145     }
146     for (auto& param : paramsVector) {
147         Framework::RemoveHeadTailSpace(param);
148     }
149     float velocity = StringUtils::StringToFloat(paramsVector[0]);
150     float mass = StringUtils::StringToFloat(paramsVector[1]);
151     float stiffness = StringUtils::StringToFloat(paramsVector[2]);
152     float damping = StringUtils::StringToFloat(paramsVector[3]);
153     // input velocity is normalized velocity, while the velocity of arkui's springMotion is absolute velocity.
154     velocity = velocity * (option->end - option->begin);
155     if (LessOrEqual(mass, 0)) {
156         mass = 1.0f;
157     }
158     if (LessOrEqual(stiffness, 0)) {
159         stiffness = 1.0f;
160     }
161     if (LessOrEqual(damping, 0)) {
162         damping = 1.0f;
163     }
164     return AceType::MakeRefPtr<SpringMotion>(
165         option->begin, option->end, velocity, AceType::MakeRefPtr<SpringProperty>(mass, stiffness, damping));
166 }
167 
ParseAnimatorOption(napi_env env,napi_callback_info info,std::shared_ptr<AnimatorOption> & option)168 static void ParseAnimatorOption(napi_env env, napi_callback_info info, std::shared_ptr<AnimatorOption>& option)
169 {
170     size_t argc = 1;
171     napi_value argv;
172     napi_get_cb_info(env, info, &argc, &argv, NULL, NULL);
173     if (argc != 1) {
174         NapiThrow(env, "The number of parameters must be equal to 1.", ERROR_CODE_PARAM_INVALID);
175         return;
176     }
177     napi_value durationNapi = nullptr;
178     napi_value easingNapi = nullptr;
179     napi_value delayNapi = nullptr;
180     napi_value fillNapi = nullptr;
181     napi_value directionNapi = nullptr;
182     napi_value iterationsNapi = nullptr;
183     napi_value beginNapi = nullptr;
184     napi_value endNapi = nullptr;
185     napi_valuetype valueType = napi_undefined;
186     napi_typeof(env, argv, &valueType);
187     if (valueType == napi_object) {
188         napi_get_named_property(env, argv, "duration", &durationNapi);
189         napi_get_named_property(env, argv, "easing", &easingNapi);
190         napi_get_named_property(env, argv, "delay", &delayNapi);
191         napi_get_named_property(env, argv, "fill", &fillNapi);
192         napi_get_named_property(env, argv, "direction", &directionNapi);
193         napi_get_named_property(env, argv, "iterations", &iterationsNapi);
194         napi_get_named_property(env, argv, "begin", &beginNapi);
195         napi_get_named_property(env, argv, "end", &endNapi);
196     } else {
197         NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
198         return;
199     }
200 
201     int32_t duration = 0;
202     int32_t delay = 0;
203     int32_t iterations = 0;
204     double begin = 0.0;
205     double end = 0.0;
206     std::string easing = "ease";
207     std::string fill = "none";
208     std::string direction = "normal";
209     ParseString(env, easingNapi, easing);
210     ParseString(env, fillNapi, fill);
211     ParseString(env, directionNapi, direction);
212     ParseInt(env, durationNapi, duration);
213     ParseInt(env, delayNapi, delay);
214     ParseInt(env, iterationsNapi, iterations);
215     ParseDouble(env, beginNapi, begin);
216     ParseDouble(env, endNapi, end);
217     option->duration = std::max(duration, 0);
218     option->delay = std::max(delay, 0);
219     option->iterations = iterations >= -1 ? iterations : 1;
220     option->begin = begin;
221     option->end = end;
222     option->easing = easing;
223     option->fill = fill;
224     option->direction = direction;
225 }
226 
GetAnimatorResult(napi_env env,napi_callback_info info)227 static AnimatorResult* GetAnimatorResult(napi_env env, napi_callback_info info)
228 {
229     AnimatorResult* animatorResult = nullptr;
230     napi_value thisVar;
231     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
232     napi_unwrap(env, thisVar, reinterpret_cast<void**>(&animatorResult));
233     return animatorResult;
234 }
235 
GetAnimatorInResult(napi_env env,napi_callback_info info)236 static RefPtr<Animator> GetAnimatorInResult(napi_env env, napi_callback_info info)
237 {
238     AnimatorResult* animatorResult = GetAnimatorResult(env, info);
239     if (!animatorResult) {
240         return nullptr;
241     }
242     return animatorResult->GetAnimator();
243 }
244 
JSReset(napi_env env,napi_callback_info info)245 static napi_value JSReset(napi_env env, napi_callback_info info)
246 {
247     AnimatorResult* animatorResult = nullptr;
248     napi_value thisVar;
249     napi_get_cb_info(env, info, NULL, NULL, &thisVar, NULL);
250     napi_unwrap(env, thisVar, (void**)&animatorResult);
251     if (!animatorResult) {
252         NapiThrow(env, "Internal error. Unwrap animator result is failed.", ERROR_CODE_INTERNAL_ERROR);
253         return nullptr;
254     }
255     auto option = animatorResult->GetAnimatorOption();
256     if (!option) {
257         NapiThrow(env, "Internal error. Option is null in AnimatorResult.", ERROR_CODE_INTERNAL_ERROR);
258         return nullptr;
259     }
260     ParseAnimatorOption(env, info, option);
261     auto animator = animatorResult->GetAnimator();
262     if (!animator) {
263         NapiThrow(env, "Internal error. Animator is null in AnimatorResult.", ERROR_CODE_INTERNAL_ERROR);
264         return nullptr;
265     }
266     animator->ClearInterpolators();
267     animator->ResetIsReverse();
268     animatorResult->ApplyOption();
269     napi_ref onframeRef = animatorResult->GetOnframeRef();
270     if (onframeRef) {
271         auto onFrameCallback = [env, onframeRef](double value) {
272             napi_handle_scope scope = nullptr;
273             napi_open_handle_scope(env, &scope);
274             if (scope == nullptr) {
275                 return;
276             }
277             napi_value ret = nullptr;
278             napi_value valueNapi = nullptr;
279             napi_value onframe = nullptr;
280             auto result = napi_get_reference_value(env, onframeRef, &onframe);
281             if (result != napi_ok || onframe == nullptr) {
282                 napi_close_handle_scope(env, scope);
283                 return;
284             }
285             napi_create_double(env, value, &valueNapi);
286             napi_call_function(env, nullptr, onframe, 1, &valueNapi, &ret);
287             napi_close_handle_scope(env, scope);
288         };
289         RefPtr<Animation<double>> animation;
290         RefPtr<Motion> motion = ParseOptionToMotion(option);
291         if (motion) {
292             motion->AddListener(onFrameCallback);
293             animatorResult->SetMotion(motion);
294         } else {
295             auto curve = Framework::CreateCurve(option->easing);
296             animation = AceType::MakeRefPtr<CurveAnimation<double>>(option->begin, option->end, curve);
297             animation->AddListener(onFrameCallback);
298             animator->AddInterpolator(animation);
299             animatorResult->SetMotion(nullptr);
300         }
301     }
302     napi_value result;
303     napi_get_null(env, &result);
304     return result;
305 }
306 
307 // since API 9 deprecated
JSUpdate(napi_env env,napi_callback_info info)308 static napi_value JSUpdate(napi_env env, napi_callback_info info)
309 {
310     return JSReset(env, info);
311 }
312 
JSPlay(napi_env env,napi_callback_info info)313 static napi_value JSPlay(napi_env env, napi_callback_info info)
314 {
315     FrameTraceAdapter* ft = FrameTraceAdapter::GetInstance();
316     if (ft != nullptr) {
317         ft->SetFrameTraceLimit();
318     }
319     auto animatorResult = GetAnimatorResult(env, info);
320     if (!animatorResult) {
321         TAG_LOGW(AceLogTag::ACE_ANIMATION, "JsAnimator: cannot find animator result when call play");
322         return nullptr;
323     }
324     auto animator = animatorResult->GetAnimator();
325     if (!animator) {
326         TAG_LOGW(AceLogTag::ACE_ANIMATION, "JsAnimator: no animator is created when call play");
327         return nullptr;
328     }
329     if (!animator->HasScheduler()) {
330         auto result = animator->AttachSchedulerOnContainer();
331         if (!result) {
332             TAG_LOGW(AceLogTag::ACE_ANIMATION,
333                 "JsAnimator: play failed, animator is not bound to specific context, id:%{public}d", animator->GetId());
334             return nullptr;
335         }
336     }
337     TAG_LOGI(AceLogTag::ACE_ANIMATION, "JsAnimator: JSPlay, id:%{public}d", animator->GetId());
338     if (animatorResult->GetMotion()) {
339         animator->PlayMotion(animatorResult->GetMotion());
340     } else {
341         animator->Play();
342     }
343     animator->PrintVsyncInfoIfNeed();
344     napi_value result = nullptr;
345     napi_get_null(env, &result);
346     return result;
347 }
348 
JSFinish(napi_env env,napi_callback_info info)349 static napi_value JSFinish(napi_env env, napi_callback_info info)
350 {
351     auto animator = GetAnimatorInResult(env, info);
352     if (!animator) {
353         return nullptr;
354     }
355     animator->Finish();
356     napi_value result = nullptr;
357     napi_get_null(env, &result);
358     return result;
359 }
360 
JSPause(napi_env env,napi_callback_info info)361 static napi_value JSPause(napi_env env, napi_callback_info info)
362 {
363     auto animator = GetAnimatorInResult(env, info);
364     if (!animator) {
365         return nullptr;
366     }
367     animator->Pause();
368     napi_value result;
369     napi_get_null(env, &result);
370     return result;
371 }
372 
JSCancel(napi_env env,napi_callback_info info)373 static napi_value JSCancel(napi_env env, napi_callback_info info)
374 {
375     auto animator = GetAnimatorInResult(env, info);
376     if (!animator) {
377         return nullptr;
378     }
379     animator->Cancel();
380     napi_value result;
381     napi_get_null(env, &result);
382     return result;
383 }
384 
JSReverse(napi_env env,napi_callback_info info)385 static napi_value JSReverse(napi_env env, napi_callback_info info)
386 {
387     auto animatorResult = GetAnimatorResult(env, info);
388     if (!animatorResult) {
389         TAG_LOGW(AceLogTag::ACE_ANIMATION, "JsAnimator: cannot find animator result when call reverse");
390         return nullptr;
391     }
392     if (animatorResult->GetMotion()) {
393         TAG_LOGW(AceLogTag::ACE_ANIMATION, "JsAnimator: interpolatingSpringCurve, cannot reverse");
394         return nullptr;
395     }
396     auto animator = animatorResult->GetAnimator();
397     if (!animator) {
398         TAG_LOGW(AceLogTag::ACE_ANIMATION, "JsAnimator: no animator is created when call reverse");
399         return nullptr;
400     }
401     TAG_LOGI(AceLogTag::ACE_ANIMATION, "JsAnimator: JSReverse, id:%{public}d", animator->GetId());
402     if (!animator->HasScheduler()) {
403         auto result = animator->AttachSchedulerOnContainer();
404         if (!result) {
405             TAG_LOGW(AceLogTag::ACE_ANIMATION, "JsAnimator: reverse failed, animator is not bound to specific context");
406             return nullptr;
407         }
408     }
409     animator->Reverse();
410     napi_value result;
411     napi_get_null(env, &result);
412     return result;
413 }
414 
SetOnframe(napi_env env,napi_callback_info info)415 static napi_value SetOnframe(napi_env env, napi_callback_info info)
416 {
417     AnimatorResult* animatorResult = nullptr;
418     size_t argc = 1;
419     napi_value thisVar = nullptr;
420     napi_value onframe = nullptr;
421     napi_get_cb_info(env, info, &argc, &onframe, &thisVar, NULL);
422     napi_unwrap(env, thisVar, (void**)&animatorResult);
423     if (!animatorResult) {
424         return nullptr;
425     }
426     auto option = animatorResult->GetAnimatorOption();
427     if (!option) {
428         return nullptr;
429     }
430     auto animator = animatorResult->GetAnimator();
431     if (!animator) {
432         return nullptr;
433     }
434     animator->ClearInterpolators();
435     // convert onframe function to reference
436     napi_ref onframeRef = animatorResult->GetOnframeRef();
437     if (onframeRef) {
438         uint32_t count = 0;
439         napi_reference_unref(env, onframeRef, &count);
440     }
441     napi_create_reference(env, onframe, 1, &onframeRef);
442     animatorResult->SetOnframeRef(onframeRef);
443     auto onFrameCallback = [env, onframeRef](double value) {
444         napi_handle_scope scope = nullptr;
445         napi_open_handle_scope(env, &scope);
446         if (scope == nullptr) {
447             return;
448         }
449         napi_value ret = nullptr;
450         napi_value valueNapi = nullptr;
451         napi_value onframe = nullptr;
452         auto result = napi_get_reference_value(env, onframeRef, &onframe);
453         if (result != napi_ok || onframe == nullptr) {
454             napi_close_handle_scope(env, scope);
455             return;
456         }
457         napi_create_double(env, value, &valueNapi);
458         napi_call_function(env, nullptr, onframe, 1, &valueNapi, &ret);
459         napi_close_handle_scope(env, scope);
460     };
461     RefPtr<Animation<double>> animation;
462     RefPtr<Motion> motion = ParseOptionToMotion(option);
463     if (motion) {
464         motion->AddListener(onFrameCallback);
465         animatorResult->SetMotion(motion);
466     } else {
467         auto curve = Framework::CreateCurve(option->easing);
468         animation = AceType::MakeRefPtr<CurveAnimation<double>>(option->begin, option->end, curve);
469         animation->AddListener(onFrameCallback);
470         animator->AddInterpolator(animation);
471         animatorResult->SetMotion(nullptr);
472     }
473     if (!animator->HasScheduler()) {
474         animator->AttachSchedulerOnContainer();
475     }
476     napi_value undefined;
477     napi_get_undefined(env, &undefined);
478     return undefined;
479 }
480 
SetOnfinish(napi_env env,napi_callback_info info)481 static napi_value SetOnfinish(napi_env env, napi_callback_info info)
482 {
483     AnimatorResult* animatorResult = nullptr;
484     size_t argc = 1;
485     napi_value thisVar = nullptr;
486     napi_value onfinish = nullptr;
487     napi_get_cb_info(env, info, &argc, &onfinish, &thisVar, NULL);
488     napi_unwrap(env, thisVar, (void**)&animatorResult);
489     if (!animatorResult) {
490         return nullptr;
491     }
492     auto option = animatorResult->GetAnimatorOption();
493     if (!option) {
494         return nullptr;
495     }
496     auto animator = animatorResult->GetAnimator();
497     if (!animator) {
498         return nullptr;
499     }
500     // convert onfinish function to reference
501     napi_ref onfinishRef = animatorResult->GetOnfinishRef();
502     if (onfinishRef) {
503         uint32_t count = 0;
504         napi_reference_unref(env, onfinishRef, &count);
505     }
506     napi_create_reference(env, onfinish, 1, &onfinishRef);
507     animatorResult->SetOnfinishRef(onfinishRef);
508     animator->ClearStopListeners();
509     animator->AddStopListener([env, onfinishRef] {
510         napi_handle_scope scope = nullptr;
511         napi_open_handle_scope(env, &scope);
512         if (scope == nullptr) {
513             return;
514         }
515         napi_value ret = nullptr;
516         napi_value onfinish = nullptr;
517         auto result = napi_get_reference_value(env, onfinishRef, &onfinish);
518         if (result != napi_ok || onfinish == nullptr) {
519             napi_close_handle_scope(env, scope);
520             return;
521         }
522         napi_call_function(env, NULL, onfinish, 0, NULL, &ret);
523         napi_close_handle_scope(env, scope);
524     });
525     napi_value undefined;
526     napi_get_undefined(env, &undefined);
527     return undefined;
528 }
529 
SetOncancel(napi_env env,napi_callback_info info)530 static napi_value SetOncancel(napi_env env, napi_callback_info info)
531 {
532     AnimatorResult* animatorResult = nullptr;
533     size_t argc = 1;
534     napi_value thisVar = nullptr;
535     napi_value oncancel = nullptr;
536     napi_get_cb_info(env, info, &argc, &oncancel, &thisVar, NULL);
537     napi_unwrap(env, thisVar, (void**)&animatorResult);
538     if (!animatorResult) {
539         return nullptr;
540     }
541     auto option = animatorResult->GetAnimatorOption();
542     if (!option) {
543         return nullptr;
544     }
545     auto animator = animatorResult->GetAnimator();
546     if (!animator) {
547         return nullptr;
548     }
549     // convert oncancel function to reference
550     napi_ref oncancelRef = animatorResult->GetOncancelRef();
551     if (oncancelRef) {
552         uint32_t count = 0;
553         napi_reference_unref(env, oncancelRef, &count);
554     }
555     napi_create_reference(env, oncancel, 1, &oncancelRef);
556     animatorResult->SetOncancelRef(oncancelRef);
557     animator->ClearIdleListeners();
558     animator->AddIdleListener([env, oncancelRef] {
559         napi_handle_scope scope = nullptr;
560         napi_open_handle_scope(env, &scope);
561         if (scope == nullptr) {
562             return;
563         }
564         napi_value ret = nullptr;
565         napi_value oncancel = nullptr;
566         auto result = napi_get_reference_value(env, oncancelRef, &oncancel);
567         if (result != napi_ok || oncancel == nullptr) {
568             napi_close_handle_scope(env, scope);
569             return;
570         }
571         napi_call_function(env, NULL, oncancel, 0, NULL, &ret);
572         napi_close_handle_scope(env, scope);
573     });
574     napi_value undefined;
575     napi_get_undefined(env, &undefined);
576     return undefined;
577 }
578 
SetOnrepeat(napi_env env,napi_callback_info info)579 static napi_value SetOnrepeat(napi_env env, napi_callback_info info)
580 {
581     AnimatorResult* animatorResult = nullptr;
582     size_t argc = 1;
583     napi_value thisVar = nullptr;
584     napi_value onrepeat = nullptr;
585     napi_get_cb_info(env, info, &argc, &onrepeat, &thisVar, NULL);
586     napi_unwrap(env, thisVar, (void**)&animatorResult);
587     if (!animatorResult) {
588         return nullptr;
589     }
590     auto option = animatorResult->GetAnimatorOption();
591     if (!option) {
592         return nullptr;
593     }
594     auto animator = animatorResult->GetAnimator();
595     if (!animator) {
596         return nullptr;
597     }
598     // convert onrepeat function to reference
599     napi_ref onrepeatRef = animatorResult->GetOnrepeatRef();
600     if (onrepeatRef) {
601         uint32_t count = 0;
602         napi_reference_unref(env, onrepeatRef, &count);
603     }
604     napi_create_reference(env, onrepeat, 1, &onrepeatRef);
605     animatorResult->SetOnrepeatRef(onrepeatRef);
606     animator->ClearRepeatListeners();
607     animator->AddRepeatListener([env, onrepeatRef] {
608         napi_handle_scope scope = nullptr;
609         napi_open_handle_scope(env, &scope);
610         if (scope == nullptr) {
611             return;
612         }
613         napi_value ret = nullptr;
614         napi_value onrepeat = nullptr;
615         auto result = napi_get_reference_value(env, onrepeatRef, &onrepeat);
616         if (result != napi_ok || onrepeat == nullptr) {
617             napi_close_handle_scope(env, scope);
618             return;
619         }
620         napi_call_function(env, NULL, onrepeat, 0, NULL, &ret);
621         napi_close_handle_scope(env, scope);
622     });
623     napi_value undefined;
624     napi_get_undefined(env, &undefined);
625     return undefined;
626 }
627 
JSCreate(napi_env env,napi_callback_info info)628 static napi_value JSCreate(napi_env env, napi_callback_info info)
629 {
630     auto option = std::make_shared<AnimatorOption>();
631     ParseAnimatorOption(env, info, option);
632     auto animator = CREATE_ANIMATOR("ohos.animator");
633     animator->AttachSchedulerOnContainer();
634     AnimatorResult* animatorResult = new AnimatorResult(animator, option);
635     napi_value jsAnimator = nullptr;
636     napi_create_object(env, &jsAnimator);
637     napi_wrap(
638         env, jsAnimator, animatorResult,
639         [](napi_env env, void* data, void* hint) {
640             AnimatorResult* animatorResult = (AnimatorResult*)data;
641             // release four references(onFunc) before releasing animatorResult
642             animatorResult->Destroy(env);
643             delete animatorResult;
644         },
645         nullptr, nullptr);
646     napi_property_descriptor resultFuncs[] = {
647         DECLARE_NAPI_FUNCTION("update", JSUpdate),
648         DECLARE_NAPI_FUNCTION("reset", JSReset),
649         DECLARE_NAPI_FUNCTION("play", JSPlay),
650         DECLARE_NAPI_FUNCTION("finish", JSFinish),
651         DECLARE_NAPI_FUNCTION("pause", JSPause),
652         DECLARE_NAPI_FUNCTION("cancel", JSCancel),
653         DECLARE_NAPI_FUNCTION("reverse", JSReverse),
654         DECLARE_NAPI_SETTER("onframe", SetOnframe),
655         DECLARE_NAPI_SETTER("onfinish", SetOnfinish),
656         DECLARE_NAPI_SETTER("oncancel", SetOncancel),
657         DECLARE_NAPI_SETTER("onrepeat", SetOnrepeat),
658     };
659 
660     NAPI_CALL(env, napi_define_properties(env, jsAnimator, sizeof(resultFuncs) / sizeof(resultFuncs[0]), resultFuncs));
661     return jsAnimator;
662 }
663 
664 // since API 9 deprecated
JSCreateAnimator(napi_env env,napi_callback_info info)665 static napi_value JSCreateAnimator(napi_env env, napi_callback_info info)
666 {
667     return JSCreate(env, info);
668 }
669 
AnimatorExport(napi_env env,napi_value exports)670 static napi_value AnimatorExport(napi_env env, napi_value exports)
671 {
672     napi_property_descriptor animatorDesc[] = {
673         DECLARE_NAPI_FUNCTION("create", JSCreate),
674         DECLARE_NAPI_FUNCTION("createAnimator", JSCreateAnimator),
675     };
676     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(animatorDesc) / sizeof(animatorDesc[0]), animatorDesc));
677     return exports;
678 }
679 
680 static napi_module animatorModule = {
681     .nm_version = 1,
682     .nm_flags = 0,
683     .nm_filename = nullptr,
684     .nm_register_func = AnimatorExport,
685     .nm_modname = "animator",
686     .nm_priv = ((void*)0),
687     .reserved = { 0 },
688 };
689 
AnimatorRegister()690 extern "C" __attribute__((constructor)) void AnimatorRegister()
691 {
692     napi_module_register(&animatorModule);
693 }
694 
ApplyOption()695 void AnimatorResult::ApplyOption()
696 {
697     CHECK_NULL_VOID(animator_);
698     CHECK_NULL_VOID(option_);
699     if (motion_) {
700         // duration not works. Iteration can only be 1. Direction can only be normal.
701         animator_->SetIteration(1);
702         animator_->SetAnimationDirection(AnimationDirection::NORMAL);
703     } else {
704         animator_->SetDuration(option_->duration);
705         animator_->SetIteration(option_->iterations);
706         animator_->SetAnimationDirection(StringToAnimationDirection(option_->direction));
707     }
708     animator_->SetStartDelay(option_->delay);
709     // FillMode not works for motion in animator implementation.
710     animator_->SetFillMode(StringToFillMode(option_->fill));
711 }
712 
Destroy(napi_env env)713 void AnimatorResult::Destroy(napi_env env)
714 {
715     if (animator_) {
716         if (!animator_->IsStopped()) {
717             animator_->Stop();
718             LOGI("Animator force stopping done, id:%{public}d", animator_->GetId());
719         }
720     }
721     if (onframe_ != nullptr) {
722         napi_delete_reference(env, onframe_);
723     }
724     if (onfinish_ != nullptr) {
725         napi_delete_reference(env, onfinish_);
726     }
727     if (oncancel_ != nullptr) {
728         napi_delete_reference(env, oncancel_);
729     }
730     if (onrepeat_ != nullptr) {
731         napi_delete_reference(env, onrepeat_);
732     }
733 }
734 
735 } // namespace OHOS::Ace::Napi
736