• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 "bridge/declarative_frontend/jsview/js_view_context.h"
17 
18 #include <functional>
19 #include <memory>
20 #include <sstream>
21 
22 #include "base/log/ace_trace.h"
23 #include "base/log/jank_frame_report.h"
24 #include "base/utils/system_properties.h"
25 #include "base/utils/utils.h"
26 #include "bridge/common/utils/engine_helper.h"
27 #include "bridge/common/utils/utils.h"
28 #include "bridge/declarative_frontend/engine/functions/js_function.h"
29 #include "bridge/declarative_frontend/jsview/models/view_context_model_impl.h"
30 #include "core/common/ace_engine.h"
31 #include "core/components/common/properties/animation_option.h"
32 #include "core/components_ng/base/view_stack_model.h"
33 #include "core/components_ng/base/view_stack_processor.h"
34 #include "core/components_ng/pattern/view_context/view_context_model_ng.h"
35 
36 #ifdef USE_ARK_ENGINE
37 #include "bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
38 #endif
39 
40 namespace OHOS::Ace {
41 
42 std::unique_ptr<ViewContextModel> ViewContextModel::instance_ = nullptr;
43 std::mutex ViewContextModel::mutex_;
44 
GetInstance()45 ViewContextModel* ViewContextModel::GetInstance()
46 {
47     if (!instance_) {
48         std::lock_guard<std::mutex> lock(mutex_);
49         if (!instance_) {
50 #ifdef NG_BUILD
51             instance_.reset(new NG::ViewContextModelNG());
52 #else
53             if (Container::IsCurrentUseNewPipeline()) {
54                 instance_.reset(new NG::ViewContextModelNG());
55             } else {
56                 instance_.reset(new Framework::ViewContextModelImpl());
57             }
58 #endif
59         }
60     }
61     return instance_.get();
62 }
63 
64 } // namespace OHOS::Ace
65 
66 namespace OHOS::Ace::Framework {
67 namespace {
68 
69 constexpr uint32_t DEFAULT_DURATION = 1000; // ms
70 constexpr int64_t MICROSEC_TO_MILLISEC = 1000;
71 
AnimateToForStageMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,JSRef<JSFunc> jsAnimateToFunc,std::function<void ()> & onFinishEvent,bool immediately)72 void AnimateToForStageMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
73     JSRef<JSFunc> jsAnimateToFunc, std::function<void()>& onFinishEvent, bool immediately)
74 {
75     auto triggerId = Container::CurrentIdSafely();
76     AceEngine::Get().NotifyContainers([triggerId, option](const RefPtr<Container>& container) {
77         auto context = container->GetPipelineContext();
78         if (!context) {
79             // pa container do not have pipeline context.
80             return;
81         }
82         if (!container->GetSettings().usingSharedRuntime) {
83             return;
84         }
85         if (!container->IsFRSCardContainer() && !container->WindowIsShow()) {
86             return;
87         }
88         ContainerScope scope(container->GetInstanceId());
89         context->FlushBuild();
90         if (context->GetInstanceId() == triggerId) {
91             return;
92         }
93         context->PrepareOpenImplicitAnimation();
94     });
95     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), onFinishEvent);
96     pipelineContext->SetSyncAnimationOption(option);
97     // Execute the function.
98     jsAnimateToFunc->Call(jsAnimateToFunc);
99     AceEngine::Get().NotifyContainers([triggerId](const RefPtr<Container>& container) {
100         auto context = container->GetPipelineContext();
101         if (!context) {
102             // pa container do not have pipeline context.
103             return;
104         }
105         if (!container->GetSettings().usingSharedRuntime) {
106             return;
107         }
108         if (!container->IsFRSCardContainer() && !container->WindowIsShow()) {
109             return;
110         }
111         ContainerScope scope(container->GetInstanceId());
112         context->FlushBuild();
113         if (context->GetInstanceId() == triggerId) {
114             return;
115         }
116         context->PrepareCloseImplicitAnimation();
117     });
118     pipelineContext->CloseImplicitAnimation();
119     pipelineContext->SetSyncAnimationOption(AnimationOption());
120     if (immediately) {
121         pipelineContext->FlushMessages();
122     } else {
123         pipelineContext->RequestFrame();
124     }
125 }
126 
AnimateToForFaMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,const JSCallbackInfo & info,std::function<void ()> & onFinishEvent,bool immediately)127 void AnimateToForFaMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
128     const JSCallbackInfo& info, std::function<void()>& onFinishEvent, bool immediately)
129 {
130     pipelineContext->FlushBuild();
131     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), onFinishEvent);
132     pipelineContext->SetSyncAnimationOption(option);
133     JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
134     jsAnimateToFunc->Call(info[1]);
135     pipelineContext->FlushBuild();
136     pipelineContext->CloseImplicitAnimation();
137     pipelineContext->SetSyncAnimationOption(AnimationOption());
138     if (immediately) {
139         pipelineContext->FlushMessages();
140     } else {
141         pipelineContext->RequestFrame();
142     }
143 }
144 
GetFormAnimationTimeInterval(const RefPtr<PipelineBase> & pipelineContext)145 int64_t GetFormAnimationTimeInterval(const RefPtr<PipelineBase>& pipelineContext)
146 {
147     CHECK_NULL_RETURN(pipelineContext, 0);
148     return (GetMicroTickCount() - pipelineContext->GetFormAnimationStartTime()) / MICROSEC_TO_MILLISEC;
149 }
150 
CheckIfSetFormAnimationDuration(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option)151 bool CheckIfSetFormAnimationDuration(const RefPtr<PipelineBase>& pipelineContext, const AnimationOption& option)
152 {
153     CHECK_NULL_RETURN(pipelineContext, false);
154     return pipelineContext->IsFormAnimationFinishCallback() && pipelineContext->IsFormRender() &&
155         option.GetDuration() > (DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext));
156 }
157 
ParseCallBackFunction(const JSRef<JSObject> & curveObj)158 std::function<float(float)> ParseCallBackFunction(const JSRef<JSObject>& curveObj)
159 {
160     std::function<float(float)> customCallBack = nullptr;
161     JSRef<JSVal> onCallBack = curveObj->GetProperty("__curveCustomFunc");
162     if (onCallBack->IsFunction()) {
163         WeakPtr<NG::FrameNode> frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
164         RefPtr<JsFunction> jsFuncCallBack =
165             AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCallBack));
166         customCallBack = [func = std::move(jsFuncCallBack), id = Container::CurrentIdSafely(), node = frameNode](
167                              float time) -> float {
168             ContainerScope scope(id);
169             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
170             CHECK_NULL_RETURN(pipelineContext, 1.0f);
171             pipelineContext->UpdateCurrentActiveNode(node);
172             JSRef<JSVal> params[1];
173             params[0] = JSRef<JSVal>::Make(ToJSValue(time));
174             auto result = func->ExecuteJS(1, params);
175             return result->IsNumber() ? result->ToNumber<float>() : 1.0f;
176         };
177     }
178     return customCallBack;
179 }
180 
181 struct KeyframeParam {
182     int32_t duration = 0;
183     RefPtr<Curve> curve;
184     std::function<void()> animationClosure;
185 };
186 
ParseKeyframeOverallParam(const JSExecutionContext & executionContext,const JSRef<JSObject> & obj)187 AnimationOption ParseKeyframeOverallParam(const JSExecutionContext& executionContext, const JSRef<JSObject>& obj)
188 {
189     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
190     AnimationOption option;
191     if (onFinish->IsFunction()) {
192         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
193         std::function<void()> onFinishEvent = [execCtx = executionContext, func = std::move(jsFunc),
194                             id = Container::CurrentIdSafely()]() mutable {
195             CHECK_NULL_VOID(func);
196             ContainerScope scope(id);
197             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
198             func->Execute();
199             func = nullptr;
200         };
201         option.SetOnFinishEvent(onFinishEvent);
202     }
203     auto delay = obj->GetPropertyValue<int32_t>("delay", 0);
204     auto iterations = obj->GetPropertyValue<int32_t>("iterations", 1);
205     option.SetDelay(delay);
206     option.SetIteration(iterations);
207     return option;
208 }
209 
ParseKeyframes(const JSExecutionContext & executionContext,const JSRef<JSArray> & arr)210 std::vector<KeyframeParam> ParseKeyframes(const JSExecutionContext& executionContext, const JSRef<JSArray>& arr)
211 {
212     std::vector<KeyframeParam> params;
213     for (size_t index = 0; index != arr->Length(); ++index) {
214         if (!arr->GetValueAt(index)->IsObject()) {
215             continue;
216         }
217         auto info = JSRef<JSObject>::Cast(arr->GetValueAt(index));
218         KeyframeParam param;
219 
220         auto jsEventValue = info->GetProperty("event");
221         if (!jsEventValue->IsFunction()) {
222             continue;
223         }
224         param.duration = info->GetPropertyValue<int32_t>("duration", DEFAULT_DURATION);
225         if (param.duration < 0) {
226             param.duration = 0;
227         }
228         RefPtr<JsFunction> jsFunc =
229             AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(jsEventValue));
230         param.animationClosure = [execCtx = executionContext, func = std::move(jsFunc)]() {
231             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
232             func->Execute();
233         };
234         auto curveArgs = info->GetProperty("curve");
235         param.curve = JSViewContext::ParseCurve(curveArgs, true);
236         params.emplace_back(param);
237     }
238     return params;
239 }
240 } // namespace
241 
ParseCurve(const JSRef<JSVal> & curveArgs,bool exceptSpring)242 RefPtr<Curve> JSViewContext::ParseCurve(const JSRef<JSVal>& curveArgs, bool exceptSpring)
243 {
244     RefPtr<Curve> curve;
245     if (curveArgs->IsString()) {
246         auto curveString = curveArgs->ToString();
247         if (exceptSpring) {
248             curve = CreateCurveExceptSpring(curveString);
249         } else {
250             curve = CreateCurve(curveString);
251         }
252     } else if (curveArgs->IsObject()) {
253         JSRef<JSObject> curveObject = JSRef<JSObject>::Cast(curveArgs);
254         JSRef<JSVal> curveString = curveObject->GetProperty("__curveString");
255         if (!curveString->IsString()) {
256             return Curves::EASE_IN_OUT;
257         }
258         auto aniTimFunc = curveString->ToString();
259         std::string customFuncName(DOM_ANIMATION_TIMING_FUNCTION_CUSTOM);
260         if (aniTimFunc == customFuncName) {
261             auto customCurveFunc = ParseCallBackFunction(curveObject);
262             curve = CreateCurve(customCurveFunc);
263         } else if (exceptSpring) {
264             curve = CreateCurveExceptSpring(aniTimFunc);
265         } else {
266             curve = CreateCurve(aniTimFunc);
267         }
268     } else {
269         curve = Curves::EASE_IN_OUT;
270     }
271     return curve;
272 }
273 
CreateAnimation(const JSRef<JSObject> & animationArgs,bool isForm)274 const AnimationOption JSViewContext::CreateAnimation(const JSRef<JSObject>& animationArgs, bool isForm)
275 {
276     AnimationOption option;
277     // If the attribute does not exist, the default value is used.
278     auto duration = animationArgs->GetPropertyValue<int32_t>("duration", DEFAULT_DURATION);
279     auto delay = animationArgs->GetPropertyValue<int32_t>("delay", 0);
280     auto iterations = animationArgs->GetPropertyValue<int32_t>("iterations", 1);
281     auto tempo = animationArgs->GetPropertyValue<double>("tempo", 1.0);
282     if (SystemProperties::GetRosenBackendEnabled() && NearZero(tempo)) {
283         // set duration to 0 to disable animation.
284         duration = 0;
285     }
286     auto direction = StringToAnimationDirection(animationArgs->GetPropertyValue<std::string>("playMode", "normal"));
287     auto finishCallbackType = static_cast<FinishCallbackType>(
288         animationArgs->GetPropertyValue<int32_t>("finishCallbackType", 0));
289     auto curve = ParseCurve(animationArgs->GetProperty("curve"));
290 
291     // limit animation for ArkTS Form
292     if (isForm) {
293         if (duration > static_cast<int32_t>(DEFAULT_DURATION)) {
294             duration = static_cast<int32_t>(DEFAULT_DURATION);
295         }
296         if (delay != 0) {
297             delay = 0;
298         }
299         if (SystemProperties::IsFormAnimationLimited() && iterations != 1) {
300             iterations = 1;
301         }
302         if (!NearEqual(tempo, 1.0)) {
303             tempo = 1.0;
304         }
305     }
306 
307     int32_t fRRmin = 0;
308     int32_t fRRmax = 0;
309     int32_t fRRExpected = 0;
310     JSRef<JSVal> rateRangeObjectArgs = animationArgs->GetProperty("expectedFrameRateRange");
311     if (rateRangeObjectArgs->IsObject()) {
312         JSRef<JSObject> rateRangeObj = JSRef<JSObject>::Cast(rateRangeObjectArgs);
313         fRRmin = rateRangeObj->GetPropertyValue<int32_t>("min", -1);
314         fRRmax = rateRangeObj->GetPropertyValue<int32_t>("max", -1);
315         fRRExpected = rateRangeObj->GetPropertyValue<int32_t>("expected", -1);
316     }
317     RefPtr<FrameRateRange> frameRateRange = AceType::MakeRefPtr<FrameRateRange>(fRRmin, fRRmax, fRRExpected);
318 
319     option.SetDuration(duration);
320     option.SetDelay(delay);
321     option.SetIteration(iterations);
322     option.SetTempo(tempo);
323     option.SetAnimationDirection(direction);
324     option.SetCurve(curve);
325     option.SetFinishCallbackType(finishCallbackType);
326     option.SetFrameRateRange(frameRateRange);
327     return option;
328 }
329 
JSAnimation(const JSCallbackInfo & info)330 void JSViewContext::JSAnimation(const JSCallbackInfo& info)
331 {
332     ACE_FUNCTION_TRACE();
333     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
334     if (!scopedDelegate) {
335         // this case usually means there is no foreground container, need to figure out the reason.
336         return;
337     }
338     if (ViewStackModel::GetInstance()->CheckTopNodeFirstBuilding()) {
339         // the node sets attribute value for the first time. No animation is generated.
340         return;
341     }
342     AnimationOption option = AnimationOption();
343     auto container = Container::CurrentSafely();
344     CHECK_NULL_VOID(container);
345     auto pipelineContextBase = container->GetPipelineContext();
346     CHECK_NULL_VOID(pipelineContextBase);
347     if (pipelineContextBase->IsFormAnimationFinishCallback() && pipelineContextBase->IsFormRender() &&
348         GetFormAnimationTimeInterval(pipelineContextBase) > DEFAULT_DURATION) {
349         TAG_LOGW(
350             AceLogTag::ACE_FORM, "[Form animation] Form finish callback triggered animation cannot exceed 1000ms.");
351         return;
352     }
353     if (info[0]->IsNull() || !info[0]->IsObject()) {
354         ViewContextModel::GetInstance()->closeAnimation(option, true);
355         return;
356     }
357     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
358     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
359     std::function<void()> onFinishEvent;
360     if (onFinish->IsFunction()) {
361         WeakPtr<NG::FrameNode> frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
362         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
363         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
364                             id = Container::CurrentIdSafely(), node = frameNode]() mutable {
365             CHECK_NULL_VOID(func);
366             ContainerScope scope(id);
367             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
368             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
369             CHECK_NULL_VOID(pipelineContext);
370             pipelineContext->UpdateCurrentActiveNode(node);
371             func->Execute();
372             func = nullptr;
373         };
374     }
375 
376     option = CreateAnimation(obj, pipelineContextBase->IsFormRender());
377     if (pipelineContextBase->IsFormAnimationFinishCallback() && pipelineContextBase->IsFormRender() &&
378         option.GetDuration() > (DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase))) {
379         option.SetDuration(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase));
380         TAG_LOGW(AceLogTag::ACE_FORM, "[Form animation]  Form animation SetDuration: %{public}lld ms",
381             static_cast<long long>(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase)));
382     }
383 
384     option.SetOnFinishEvent(onFinishEvent);
385     if (SystemProperties::GetRosenBackendEnabled()) {
386         option.SetAllowRunningAsynchronously(true);
387     }
388     ViewContextModel::GetInstance()->openAnimation(option);
389     JankFrameReport::GetInstance().ReportJSAnimation();
390 }
391 
JSAnimateTo(const JSCallbackInfo & info)392 void JSViewContext::JSAnimateTo(const JSCallbackInfo& info)
393 {
394     ACE_FUNCTION_TRACE();
395     AnimateToInner(info, false);
396 }
397 
JSAnimateToImmediately(const JSCallbackInfo & info)398 void JSViewContext::JSAnimateToImmediately(const JSCallbackInfo& info)
399 {
400     ACE_FUNCTION_TRACE();
401     AnimateToInner(info, true);
402 }
403 
AnimateToInner(const JSCallbackInfo & info,bool immediately)404 void JSViewContext::AnimateToInner(const JSCallbackInfo& info, bool immediately)
405 {
406     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
407     if (!scopedDelegate) {
408         // this case usually means there is no foreground container, need to figure out the reason.
409         return;
410     }
411     if (info.Length() < 2) {
412         return;
413     }
414     if (!info[0]->IsObject()) {
415         return;
416     }
417     // 2nd argument should be a closure passed to the animateTo function.
418     if (!info[1]->IsFunction()) {
419         return;
420     }
421 
422     auto container = Container::CurrentSafely();
423     CHECK_NULL_VOID(container);
424     auto pipelineContext = container->GetPipelineContext();
425     CHECK_NULL_VOID(pipelineContext);
426     if (pipelineContext->IsFormAnimationFinishCallback() && pipelineContext->IsFormRender() &&
427         GetFormAnimationTimeInterval(pipelineContext) > DEFAULT_DURATION) {
428         TAG_LOGW(
429             AceLogTag::ACE_FORM, "[Form animation] Form finish callback triggered animation cannot exceed 1000ms.");
430         return;
431     }
432 
433     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
434     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
435     std::function<void()> onFinishEvent;
436     auto traceStreamPtr = std::make_shared<std::stringstream>();
437     if (onFinish->IsFunction()) {
438         WeakPtr<NG::FrameNode> frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
439         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
440         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
441                             id = Container::CurrentIdSafely(), traceStreamPtr, node = frameNode]() mutable {
442             CHECK_NULL_VOID(func);
443             ContainerScope scope(id);
444             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
445             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
446             CHECK_NULL_VOID(pipelineContext);
447             pipelineContext->UpdateCurrentActiveNode(node);
448             func->Execute();
449             func = nullptr;
450             AceAsyncTraceEnd(0, traceStreamPtr->str().c_str(), true);
451         };
452     } else {
453         onFinishEvent = [traceStreamPtr]() {
454             AceAsyncTraceEnd(0, traceStreamPtr->str().c_str(), true);
455         };
456     }
457 
458     AnimationOption option = CreateAnimation(obj, pipelineContext->IsFormRender());
459     *traceStreamPtr << "AnimateTo, Options"
460                     << " duration:" << option.GetDuration()
461                     << ",iteration:" << option.GetIteration()
462                     << ",delay:" << option.GetDelay()
463                     << ",tempo:" << option.GetTempo()
464                     << ",direction:" << (uint32_t) option.GetAnimationDirection()
465                     << ",curve:" << (option.GetCurve() ? option.GetCurve()->ToString().c_str() : "");
466     AceAsyncTraceBegin(0, traceStreamPtr->str().c_str(), true);
467     if (CheckIfSetFormAnimationDuration(pipelineContext, option)) {
468         option.SetDuration(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext));
469         TAG_LOGW(AceLogTag::ACE_FORM, "[Form animation]  Form animation SetDuration: %{public}lld ms",
470             static_cast<long long>(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext)));
471     }
472     if (SystemProperties::GetRosenBackendEnabled()) {
473         bool usingSharedRuntime = container->GetSettings().usingSharedRuntime;
474         if (usingSharedRuntime) {
475             if (pipelineContext->IsLayouting()) {
476                 TAG_LOGW(AceLogTag::ACE_ANIMATION,
477                     "pipeline is layouting, post animateTo, duration:%{public}d, curve:%{public}s",
478                     option.GetDuration(), option.GetCurve() ? option.GetCurve()->ToString().c_str() : "");
479                 pipelineContext->GetTaskExecutor()->PostTask(
480                     [id = Container::CurrentIdSafely(), option, func = JSRef<JSFunc>::Cast(info[1]),
481                         onFinishEvent, immediately]() mutable {
482                         ContainerScope scope(id);
483                         auto container = Container::CurrentSafely();
484                         CHECK_NULL_VOID(container);
485                         auto pipelineContext = container->GetPipelineContext();
486                         CHECK_NULL_VOID(pipelineContext);
487                         AnimateToForStageMode(pipelineContext, option, func, onFinishEvent, immediately);
488                     },
489                     TaskExecutor::TaskType::UI);
490                 return;
491             }
492             AnimateToForStageMode(pipelineContext, option, JSRef<JSFunc>::Cast(info[1]), onFinishEvent, immediately);
493         } else {
494             AnimateToForFaMode(pipelineContext, option, info, onFinishEvent, immediately);
495         }
496     } else {
497         pipelineContext->FlushBuild();
498         pipelineContext->SaveExplicitAnimationOption(option);
499         // Execute the function.
500         JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
501         jsAnimateToFunc->Call(info[1]);
502         pipelineContext->FlushBuild();
503         pipelineContext->CreateExplicitAnimator(onFinishEvent);
504         pipelineContext->ClearExplicitAnimationOption();
505     }
506 }
507 
JSKeyframeAnimateTo(const JSCallbackInfo & info)508 void JSViewContext::JSKeyframeAnimateTo(const JSCallbackInfo& info)
509 {
510     ACE_FUNCTION_TRACE();
511     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
512     if (!scopedDelegate) {
513         // this case usually means there is no foreground container, need to figure out the reason.
514         return;
515     }
516     if (info.Length() < 2) {
517         return;
518     }
519     if (!info[0]->IsObject()) {
520         return;
521     }
522     if (!info[1]->IsArray()) {
523         return;
524     }
525     JSRef<JSArray> keyframeArr = JSRef<JSArray>::Cast(info[1]);
526     if (keyframeArr->Length() == 0) {
527         return;
528     }
529 
530     auto container = Container::CurrentSafely();
531     CHECK_NULL_VOID(container);
532     auto pipelineContext = container->GetPipelineContext();
533     CHECK_NULL_VOID(pipelineContext);
534     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
535     auto overallAnimationOption = ParseKeyframeOverallParam(info.GetExecutionContext(), obj);
536     auto keyframes = ParseKeyframes(info.GetExecutionContext(), keyframeArr);
537     int duration = 0;
538     for (auto& keyframe : keyframes) {
539         duration += keyframe.duration;
540     }
541     overallAnimationOption.SetDuration(duration);
542     // actual curve is in keyframe, this curve will not be effective
543     overallAnimationOption.SetCurve(Curves::EASE_IN_OUT);
544     pipelineContext->FlushBuild();
545     pipelineContext->OpenImplicitAnimation(
546         overallAnimationOption, overallAnimationOption.GetCurve(), overallAnimationOption.GetOnFinishEvent());
547     for (auto& keyframe : keyframes) {
548         if (!keyframe.animationClosure) {
549             continue;
550         }
551         AceTraceBeginWithArgs("keyframe duration%d", keyframe.duration);
552         AnimationUtils::AddDurationKeyFrame(keyframe.duration, keyframe.curve, [&keyframe, &pipelineContext]() {
553             keyframe.animationClosure();
554             pipelineContext->FlushBuild();
555             if (!pipelineContext->IsLayouting()) {
556                 pipelineContext->FlushUITasks();
557             } else {
558                 TAG_LOGI(AceLogTag::ACE_ANIMATION, "isLayouting, maybe some layout keyframe animation not generated");
559             }
560         });
561         AceTraceEnd();
562     }
563     pipelineContext->CloseImplicitAnimation();
564 }
565 
JSBind(BindingTarget globalObj)566 void JSViewContext::JSBind(BindingTarget globalObj)
567 {
568     JSClass<JSViewContext>::Declare("Context");
569     JSClass<JSViewContext>::StaticMethod("animation", JSAnimation);
570     JSClass<JSViewContext>::StaticMethod("animateTo", JSAnimateTo);
571     JSClass<JSViewContext>::StaticMethod("animateToImmediately", JSAnimateToImmediately);
572     JSClass<JSViewContext>::StaticMethod("keyframeAnimateTo", JSKeyframeAnimateTo);
573     JSClass<JSViewContext>::Bind<>(globalObj);
574 }
575 
576 } // namespace OHOS::Ace::Framework
577