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