• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 "frameworks/bridge/js_frontend/engine/quickjs/animator_bridge.h"
17 
18 #include <utility>
19 
20 #include "base/log/log.h"
21 #include "base/utils/string_utils.h"
22 #include "core/common/container.h"
23 #include "core/common/thread_checker.h"
24 #include "frameworks/bridge/common/utils/utils.h"
25 #include "frameworks/bridge/js_frontend/engine/quickjs/qjs_engine.h"
26 #include "frameworks/bridge/js_frontend/engine/quickjs/qjs_group_js_bridge.h"
27 #include "frameworks/bridge/js_frontend/js_ace_page.h"
28 
29 namespace OHOS::Ace::Framework {
30 namespace {
31 
GetPageById(QjsEngineInstance * instance,int32_t pageId)32 RefPtr<JsAcePage> GetPageById(QjsEngineInstance* instance, int32_t pageId)
33 {
34     LOGD("Enter GetPageById");
35     if (instance == nullptr) {
36         LOGE("instance is null.");
37         return nullptr;
38     }
39     auto delegate = instance->GetDelegate();
40     if (!delegate) {
41         LOGE("delegate is null.");
42         return nullptr;
43     }
44     return delegate->GetPage(pageId);
45 }
46 
GetJsInt32Val(JSContext * ctx,JSValueConst value)47 inline int32_t GetJsInt32Val(JSContext* ctx, JSValueConst value)
48 {
49     int32_t val = 0;
50     if (JS_IsNumber(value) && (JS_ToInt32(ctx, &val, value)) < 0) {
51         val = 0;
52     }
53     return val;
54 }
55 
HandleJsAnimatorContext(JSContext * ctx,JSValueConst value,AnimatorOperation operation)56 void HandleJsAnimatorContext(JSContext* ctx, JSValueConst value, AnimatorOperation operation)
57 {
58     QJSHandleScope handleScope(ctx);
59     int32_t bridgeId = GetJsInt32Val(ctx, QJSUtils::GetPropertyStr(ctx, value, "__bridgeId"));
60     int32_t pageId = GetJsInt32Val(ctx, QJSUtils::GetPropertyStr(ctx, value, "__pageId"));
61     auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
62     auto page = GetPageById(instance, pageId);
63     if (!page) {
64         LOGE("no page found for bridgeId: %{public}d", bridgeId);
65         return;
66     }
67     auto task = AceType::MakeRefPtr<AnimatorTaskOperation>(operation);
68     page->PushCommand(AceType::MakeRefPtr<JsCommandAnimator>(bridgeId, task));
69     if (page->CheckPageCreated()) {
70         auto delegate = instance->GetDelegate();
71         if (!delegate) {
72             LOGE("delegate is nullptr");
73             return;
74         }
75         delegate->TriggerPageUpdate(page->GetPageId());
76     }
77 }
78 
AddListenerForEventCallback(const WeakPtr<AnimatorBridge> & bridgeWeak,const RefPtr<Animator> & animator,JSContext * ctx)79 void AddListenerForEventCallback(const WeakPtr<AnimatorBridge>& bridgeWeak,
80     const RefPtr<Animator>& animator, JSContext* ctx)
81 {
82     animator->AddStartListener([ctx, bridgeWeak] {
83         auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
84         auto jsTaskExecutor = instance->GetDelegate()->GetAnimationJsTask();
85         jsTaskExecutor.PostTask([bridgeWeak, instance]() mutable {
86             auto bridge = bridgeWeak.Upgrade();
87             if (!bridge) {
88                 return;
89             }
90             LOGI("call animation onstart event");
91             instance->CallAnimationStartJs(bridge->GetJsObject());
92         });
93     });
94     animator->AddStopListener([ctx, bridgeWeak] {
95         auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
96         auto jsTaskExecutor = instance->GetDelegate()->GetAnimationJsTask();
97         jsTaskExecutor.PostTask([bridgeWeak, instance]() mutable {
98             auto bridge = bridgeWeak.Upgrade();
99             if (!bridge) {
100                 return;
101             }
102             LOGI("call animation onfinish event");
103             instance->CallAnimationFinishJs(bridge->GetJsObject());
104         });
105     });
106     animator->AddIdleListener([ctx, bridgeWeak] {
107         auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
108         auto jsTaskExecutor = instance->GetDelegate()->GetAnimationJsTask();
109         jsTaskExecutor.PostTask([bridgeWeak, instance]() mutable {
110             auto bridge = bridgeWeak.Upgrade();
111             if (!bridge) {
112                 return;
113             }
114             LOGI("call animation oncancel event");
115             instance->CallAnimationCancelJs(bridge->GetJsObject());
116         });
117     });
118     animator->AddRepeatListener([ctx, bridgeWeak] {
119         auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
120         auto jsTaskExecutor = instance->GetDelegate()->GetAnimationJsTask();
121         jsTaskExecutor.PostTask([bridgeWeak, instance]() mutable {
122             auto bridge = bridgeWeak.Upgrade();
123             if (!bridge) {
124                 return;
125             }
126             LOGI("call animation onrepeat event");
127             instance->CallAnimationRepeatJs(bridge->GetJsObject());
128         });
129     });
130 }
131 
AddFrameListener(const WeakPtr<AnimatorBridge> & bridgeWeak,const RefPtr<KeyframeAnimation<double>> & animation,JSContext * ctx)132 void AddFrameListener(const WeakPtr<AnimatorBridge>& bridgeWeak, const RefPtr<KeyframeAnimation<double>>& animation,
133     JSContext* ctx)
134 {
135     animation->AddListener([ctx, bridgeWeak](double value) {
136         auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
137         auto jsTaskExecutor = instance->GetDelegate()->GetAnimationJsTask();
138         jsTaskExecutor.PostTask([bridgeWeak, instance, value]() mutable {
139             auto bridge = bridgeWeak.Upgrade();
140             if (!bridge) {
141                 return;
142             }
143             std::string arg = std::to_string(value);
144             instance->CallAnimationFrameJs(bridge->GetJsObject(), arg.c_str());
145         });
146     });
147 }
148 }
149 
CreateAnimatorContext(JSContext * ctx,int32_t pageId,int32_t bridgeId)150 JSValue AnimatorBridgeUtils::CreateAnimatorContext(JSContext* ctx, int32_t pageId, int32_t bridgeId)
151 {
152     auto animatorContext = JS_NewObject(ctx);
153     JS_SetPropertyStr(ctx, animatorContext, "play", JS_NewCFunction(ctx, JsAnimatorPlay, "play", 0));
154     JS_SetPropertyStr(ctx, animatorContext, "finish", JS_NewCFunction(ctx, JsAnimatorFinish, "finish", 0));
155     JS_SetPropertyStr(ctx, animatorContext, "pause", JS_NewCFunction(ctx, JsAnimatorPause, "pause", 0));
156     JS_SetPropertyStr(ctx, animatorContext, "cancel", JS_NewCFunction(ctx, JsAnimatorCancel, "cancel", 0));
157     JS_SetPropertyStr(ctx, animatorContext, "reverse", JS_NewCFunction(ctx, JsAnimatorReverse, "reverse", 0));
158     JS_SetPropertyStr(ctx, animatorContext, "update", JS_NewCFunction(ctx, JsAnimatorUpdate, "updateOption", 1));
159     JS_SetPropertyStr(ctx, animatorContext, "__pageId", JS_NewInt32(ctx, pageId));
160     JS_SetPropertyStr(ctx, animatorContext, "__bridgeId", JS_NewInt32(ctx, bridgeId));
161     return animatorContext;
162 }
163 
JsAnimatorPlay(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)164 JSValue AnimatorBridgeUtils::JsAnimatorPlay(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
165 {
166     if ((!argv) || (argc != 0)) {
167         LOGE("argc error, argc = %{private}d", argc);
168         return JS_NULL;
169     }
170     HandleJsAnimatorContext(ctx, value, AnimatorOperation::PLAY);
171     return JS_NULL;
172 }
173 
JsAnimatorFinish(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)174 JSValue AnimatorBridgeUtils::JsAnimatorFinish(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
175 {
176     if ((!argv) || (argc != 0)) {
177         LOGE("argc error, argc = %{private}d", argc);
178         return JS_NULL;
179     }
180     HandleJsAnimatorContext(ctx, value, AnimatorOperation::FINISH);
181     return JS_NULL;
182 }
183 
JsAnimatorPause(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)184 JSValue AnimatorBridgeUtils::JsAnimatorPause(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
185 {
186     if ((!argv) || (argc != 0)) {
187         LOGE("argc error, argc = %{private}d", argc);
188         return JS_NULL;
189     }
190     HandleJsAnimatorContext(ctx, value, AnimatorOperation::PAUSE);
191     return JS_NULL;
192 }
193 
JsAnimatorCancel(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)194 JSValue AnimatorBridgeUtils::JsAnimatorCancel(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
195 {
196     if ((!argv) || (argc != 0)) {
197         LOGE("argc error, argc = %{private}d", argc);
198         return JS_NULL;
199     }
200     HandleJsAnimatorContext(ctx, value, AnimatorOperation::CANCEL);
201     return JS_NULL;
202 }
203 
JsAnimatorReverse(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)204 JSValue AnimatorBridgeUtils::JsAnimatorReverse(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
205 {
206     if ((!argv) || (argc != 0)) {
207         LOGE("argc error, argc = %{private}d", argc);
208         return JS_NULL;
209     }
210     HandleJsAnimatorContext(ctx, value, AnimatorOperation::REVERSE);
211     return JS_NULL;
212 }
213 
JsAnimatorUpdate(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)214 JSValue AnimatorBridgeUtils::JsAnimatorUpdate(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
215 {
216     if ((!argv) || (argc != 1)) {
217         LOGE("argc error, argc = %{private}d", argc);
218         ThrowJsError(ctx, "Parameter error. The type of the parameter 1 is not object.", ERROR_CODE_PARAM_INVALID);
219         return JS_NULL;
220     }
221     JSPropertyEnum* pTab = nullptr;
222     uint32_t len = 0;
223     if (!CheckAndGetJsProperty(ctx, argv[0], &pTab, &len)) {
224         return JS_NULL;
225     }
226     std::unordered_map<std::string, std::string> params;
227     for (uint32_t i = 0; i < len; ++i) {
228         const char* key = JS_AtomToCString(ctx, pTab[i].atom);
229         if (key == nullptr) {
230             JS_FreeAtom(ctx, pTab[i].atom);
231             LOGW("key is null. Ignoring!");
232             continue;
233         }
234         JSValue valItem = JS_GetProperty(ctx, argv[0], pTab[i].atom);
235         if (JS_IsString(valItem) || JS_IsNumber(valItem)) {
236             ScopedString styleVal(ctx, valItem);
237             const char* valStr = styleVal.get();
238             params[key] = valStr;
239         } else {
240             LOGD("value of unsupported type. Ignoring!");
241         }
242     }
243     int32_t pageId = GetJsInt32Val(ctx, QJSUtils::GetPropertyStr(ctx, value, "__pageId"));
244     int32_t bridgeId = GetJsInt32Val(ctx, QJSUtils::GetPropertyStr(ctx, value, "__bridgeId"));
245     auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
246     auto page = GetPageById(instance, pageId);
247     if (!page) {
248         LOGE("no page found for bridgeId: %{public}d", bridgeId);
249         ThrowJsError(ctx, "Internal error. Can not find the page for pageId.", ERROR_CODE_INTERNAL_ERROR);
250         return JS_NULL;
251     }
252     auto task = AceType::MakeRefPtr<AnimatorTaskUpdate>(params);
253     page->PushCommand(AceType::MakeRefPtr<JsCommandAnimator>(bridgeId, task));
254     return JS_NULL;
255 }
256 
JsCreateBridgeId()257 int32_t AnimatorBridgeUtils::JsCreateBridgeId()
258 {
259     static int32_t bridgeId = 0;
260     return bridgeId++;
261 }
262 
AnimatorBridge(JSContext * ctx,JSValue animationContext)263 AnimatorBridge::AnimatorBridge(JSContext* ctx, JSValue animationContext)
264     : ctx_(ctx), animationContext_(JS_DupValue(ctx, animationContext))
265 {
266     if (ctx_ == nullptr) {
267         return;
268     }
269     auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx_));
270     if (instance == nullptr) {
271         return;
272     }
273 }
274 
~AnimatorBridge()275 AnimatorBridge::~AnimatorBridge()
276 {
277     // when last page exit, js engine will destruct first, so do not free JSObject again.
278     CHECK_RUN_ON(JS);
279     if (ctx_ != nullptr) {
280         JS_FreeValue(ctx_, animationContext_);
281         ctx_ = nullptr;
282     }
283     RefPtr<Animator> animator;
284     animator.Swap(animator_);
285     auto taskExecutor = Container::CurrentTaskExecutor();
286     if (taskExecutor) {
287         taskExecutor->PostSyncTask(
288             [&animator]() {
289                 LOGI("release animator on UI thread");
290                 animator.Reset();
291             },
292             TaskExecutor::TaskType::UI);
293     }
294 }
295 
OnJsEngineDestroy()296 void AnimatorBridge::OnJsEngineDestroy()
297 {
298     CHECK_RUN_ON(JS);
299     if (ctx_ != nullptr) {
300         JS_FreeValue(ctx_, animationContext_);
301         ctx_ = nullptr;
302     }
303 }
304 
JsCreateAnimation(const std::string & param)305 void AnimatorBridge::JsCreateAnimation(const std::string& param)
306 {
307     int32_t iterations = 1;
308     double duration = 0.0;
309     double delay = 0.0;
310     std::unordered_map<std::string, double> animationDoubleParams;
311     std::unordered_map<std::string, std::string> animationStringParams;
312     BaseAnimationBridgeUtils::JsParseAnimatorParams(param, iterations, animationDoubleParams, animationStringParams);
313     RefPtr<Curve> curve;
314     std::string curveString;
315     auto iterEasing = animationStringParams.find(DOM_ANIMATION_EASING);
316     if (iterEasing != animationStringParams.end()) {
317         curveString = iterEasing->second;
318     }
319     curve = CreateCurve(curveString);
320     auto keyframeAnimation = CreateDoubleAnimation(animationDoubleParams, curve);
321     if (!ctx_ || !keyframeAnimation) {
322         LOGE("animation create failed");
323         return;
324     }
325     auto iterDuration = animationDoubleParams.find(DOM_ANIMATION_DURATION_API);
326     if (iterDuration != animationDoubleParams.end()) {
327         duration = iterDuration->second;
328     }
329     std::string fillString;
330     auto iterFill = animationStringParams.find(DOM_ANIMATION_FILL);
331     if (iterFill != animationStringParams.end()) {
332         fillString = iterFill->second;
333     }
334     auto iterDelay = animationDoubleParams.find(DOM_ANIMATION_DELAY_API);
335     if (iterDelay != animationDoubleParams.end()) {
336         delay = iterDelay->second;
337     }
338     AddFrameListener(AceType::WeakClaim(this), keyframeAnimation, ctx_);
339     if (!animator_) {
340         animator_ = AceType::MakeRefPtr<Animator>();
341     }
342     auto iterDirection = animationStringParams.find(DOM_ANIMATION_DIRECTION_API);
343     if (iterDirection != animationStringParams.end()) {
344         animator_->SetAnimationDirection(StringToAnimationDirection(iterDirection->second));
345     }
346     if (!animator_->IsStopped()) {
347         animator_->Stop();
348     }
349     animator_->ClearInterpolators();
350     animator_->SetDuration(duration);
351     animator_->SetIteration(iterations);
352     animator_->SetStartDelay(delay);
353     animator_->SetFillMode(StringToFillMode(fillString));
354     animator_->AddInterpolator(keyframeAnimation);
355     AddListenerForEventCallback(AceType::WeakClaim(this), animator_, ctx_);
356 }
357 
CreateDoubleAnimation(const std::unordered_map<std::string,double> & animationParams,const RefPtr<Curve> & curve)358 RefPtr<KeyframeAnimation<double>> AnimatorBridge::CreateDoubleAnimation(
359     const std::unordered_map<std::string, double>& animationParams, const RefPtr<Curve>& curve)
360 {
361     double begin = 0.0;
362     double end = 1.0;
363     auto animationBegin = animationParams.find(DOM_ANIMATION_BEGIN);
364     if (animationBegin != animationParams.end()) {
365         begin = animationBegin->second;
366     }
367     auto animationEnd = animationParams.find(DOM_ANIMATION_END);
368     if (animationEnd != animationParams.end()) {
369         end = animationEnd->second;
370     }
371     auto keyframeBegin = AceType::MakeRefPtr<Keyframe<double>>(0.0, begin);
372     auto keyframeEnd = AceType::MakeRefPtr<Keyframe<double>>(1.0, end);
373     auto keyframeAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
374     keyframeAnimation->AddKeyframe(keyframeBegin);
375     keyframeAnimation->AddKeyframe(keyframeEnd);
376     keyframeAnimation->SetCurve(curve);
377     return keyframeAnimation;
378 }
379 
AnimatorTaskCreate(const RefPtr<AnimatorBridge> & bridge,const std::string & param)380 AnimatorTaskCreate::AnimatorTaskCreate(const RefPtr<AnimatorBridge>& bridge, const std::string& param)
381     : bridge_(bridge), param_(std::move(param))
382 {}
383 
AnimatorBridgeTaskFunc(const RefPtr<JsAcePage> & page,int32_t bridgeId)384 void AnimatorTaskCreate::AnimatorBridgeTaskFunc(const RefPtr<JsAcePage>& page, int32_t bridgeId)
385 {
386     if (!bridge_ || !page) {
387         LOGE("Create Animation Bridge failed. bridge is null.");
388         return;
389     }
390     page->RemoveAnimatorBridge(bridgeId);
391     bridge_->JsCreateAnimation(param_);
392     page->AddAnimatorBridge(bridgeId, bridge_);
393 }
394 
AnimatorBridgeTaskFunc(const RefPtr<JsAcePage> & page,int32_t bridgeId)395 void AnimatorTaskOperation::AnimatorBridgeTaskFunc(const RefPtr<JsAcePage>& page, int32_t bridgeId)
396 {
397     auto animatorBridge = AceType::DynamicCast<AnimatorBridge>(page->GetAnimatorBridge(bridgeId));
398     if (!animatorBridge) {
399         LOGE("no animation bridge found for bridgeId: %{public}d", bridgeId);
400         return;
401     }
402     RefPtr<Animator> animator = animatorBridge->JsGetAnimator();
403     if (!animator) {
404         LOGE("animator is null");
405         return;
406     }
407     switch (operation_) {
408         case AnimatorOperation::PLAY:
409             animator->Play();
410             break;
411         case AnimatorOperation::PAUSE:
412             animator->Pause();
413             break;
414         case AnimatorOperation::CANCEL:
415             animator->Cancel();
416             break;
417         case AnimatorOperation::FINISH:
418             animator->Finish();
419             break;
420         case AnimatorOperation::REVERSE:
421             animator->Reverse();
422             break;
423         case AnimatorOperation::NONE:
424         default:
425             break;
426     }
427 }
428 
AnimatorBridgeTaskFunc(const RefPtr<JsAcePage> & page,int32_t bridgeId)429 void AnimatorTaskUpdate::AnimatorBridgeTaskFunc(const RefPtr<JsAcePage>& page, int32_t bridgeId)
430 {
431     auto animatorBridge = AceType::DynamicCast<AnimatorBridge>(page->GetAnimatorBridge(bridgeId));
432     if (!animatorBridge) {
433         LOGE("no animation bridge found for bridgeId: %{public}d", bridgeId);
434         return;
435     }
436     RefPtr<Animator> animator = animatorBridge->JsGetAnimator();
437     if (!animator) {
438         LOGE("animator is null");
439         return;
440     }
441     JSContext* ctx = animatorBridge->GetContext();
442     if (!ctx) {
443         LOGE("ctx is null");
444         return;
445     }
446     animator->ClearInterpolators();
447     UpdateAnimator(animator, animatorBridge, ctx, params_);
448 }
449 
UpdateAnimator(const RefPtr<Animator> & animator,const RefPtr<AnimatorBridge> & bridge,JSContext * ctx,const std::unordered_map<std::string,std::string> & params)450 void AnimatorTaskUpdate::UpdateAnimator(const RefPtr<Animator>& animator, const RefPtr<AnimatorBridge>& bridge,
451     JSContext* ctx, const std::unordered_map<std::string, std::string>& params)
452 {
453     int32_t iterations = 1;
454     double duration = 0.0;
455     double delay = 0.0;
456     double begin = 0.0;
457     double end = 1.0;
458     std::string curveString;
459     std::string fillString;
460     RefPtr<Curve> curve;
461     auto iterEasing = params_.find(DOM_ANIMATION_EASING);
462     if (iterEasing != params_.end()) {
463         curveString = iterEasing->second;
464     }
465     curve = CreateCurve(curveString);
466     auto iterIterations = params_.find(DOM_ANIMATION_ITERATIONS);
467     if (iterIterations != params_.end()) {
468         iterations = StringToInt(iterIterations->second);
469     }
470     auto iterDuration = params_.find(DOM_ANIMATION_DURATION_API);
471     if (iterDuration != params_.end()) {
472         duration = StringToDouble(iterDuration->second);
473     }
474     auto iterFill = params_.find(DOM_ANIMATION_FILL);
475     if (iterFill != params_.end()) {
476         fillString = iterFill->second;
477     }
478     auto iterDelay = params_.find(DOM_ANIMATION_DELAY_API);
479     if (iterDelay != params_.end()) {
480         delay = StringToDouble(iterDelay->second);
481     }
482     auto iterDirection = params_.find(DOM_ANIMATION_DIRECTION_API);
483     if (iterDirection != params_.end()) {
484         animator->SetAnimationDirection(StringToAnimationDirection(iterDirection->second));
485     }
486     auto animationBegin = params_.find(DOM_ANIMATION_BEGIN);
487     if (animationBegin != params_.end()) {
488         begin = StringToDouble(animationBegin->second);
489     }
490     auto animationEnd = params_.find(DOM_ANIMATION_END);
491     if (animationEnd != params_.end()) {
492         end = StringToDouble(animationEnd->second);
493     }
494     auto keyframeAnimation = CreateDoubleAnimation(begin, end, curve);
495     if (!keyframeAnimation) {
496         LOGE("animation create failed");
497         return;
498     }
499     AddFrameListener(AceType::WeakClaim(RawPtr(bridge)), keyframeAnimation, ctx);
500     animator->SetDuration(duration);
501     animator->SetIteration(iterations);
502     animator->SetStartDelay(delay);
503     animator->SetFillMode(StringToFillMode(fillString));
504     animator->AddInterpolator(keyframeAnimation);
505 }
506 
CreateDoubleAnimation(double begin,double end,const RefPtr<Curve> & curve)507 RefPtr<KeyframeAnimation<double>> AnimatorTaskUpdate::CreateDoubleAnimation(double begin, double end,
508     const RefPtr<Curve>& curve)
509 {
510     auto keyframeBegin = AceType::MakeRefPtr<Keyframe<double>>(0.0, begin);
511     auto keyframeEnd = AceType::MakeRefPtr<Keyframe<double>>(1.0, end);
512     auto keyframeAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
513     keyframeAnimation->AddKeyframe(keyframeBegin);
514     keyframeAnimation->AddKeyframe(keyframeEnd);
515     keyframeAnimation->SetCurve(curve);
516     return keyframeAnimation;
517 }
518 
519 } // namespace OHOS::Ace::Framework