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