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