1 /*
2 * Copyright (c) 2021-2024 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_animator.h"
17 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
18
19
20 #include "base/log/ace_scoring_log.h"
21 #include "bridge/declarative_frontend/engine/functions/js_animator_function.h"
22 #include "bridge/declarative_frontend/jsview/models/animator_model_impl.h"
23 #include "core/components_ng/pattern/animator/animator_model.h"
24 #include "core/components_ng/pattern/animator/animator_model_ng.h"
25
26 namespace OHOS::Ace {
27
28 std::unique_ptr<AnimatorModel> AnimatorModel::instance_ = nullptr;
29 std::mutex AnimatorModel::mutex_;
30
GetInstance()31 AnimatorModel* AnimatorModel::GetInstance()
32 {
33 if (!instance_) {
34 std::lock_guard<std::mutex> lock(mutex_);
35 if (!instance_) {
36 #ifdef NG_BUILD
37 instance_.reset(new Framework::AnimatorModelNG());
38 #else
39 if (Container::IsCurrentUseNewPipeline()) {
40 instance_.reset(new Framework::AnimatorModelNG());
41 } else {
42 instance_.reset(new Framework::AnimatorModelImpl());
43 }
44 #endif
45 }
46 }
47 return instance_.get();
48 }
49
50 } // namespace OHOS::Ace
51
52 namespace OHOS::Ace::Framework {
53 namespace {
54
55 constexpr int32_t FRICTION_MOTION_LENGTH = 3;
56 constexpr int32_t SPRING_MOTION_LENGTH = 4;
57 constexpr int32_t SCROLL_MOTION_LENGTH = 5;
58
AddFrameListener(const RefPtr<AnimatorInfo> & animatorInfo,const RefPtr<KeyframeAnimation<double>> & animation)59 void AddFrameListener(const RefPtr<AnimatorInfo>& animatorInfo, const RefPtr<KeyframeAnimation<double>>& animation)
60 {
61 if (!animatorInfo || !animation) {
62 return;
63 }
64 auto frameEvent = animatorInfo->GetFrameEvent();
65 if (frameEvent) {
66 animation->AddListener(
67 [frameEvent, weakInfo = WeakPtr<AnimatorInfo>(animatorInfo)](const float& progress) {
68 auto animatorInfo = weakInfo.Upgrade();
69 CHECK_NULL_VOID(animatorInfo);
70 ACE_SCOPED_TRACE("animator component onframe. duration:%d, curve:%s", animatorInfo->GetDuration(),
71 animatorInfo->GetCurve() ? animatorInfo->GetCurve()->ToString().c_str() : "");
72 frameEvent(progress);
73 });
74 }
75 }
76
HandleAnimatorInfo(const RefPtr<AnimatorInfo> & animatorInfo,const RefPtr<Animator> & animator)77 void HandleAnimatorInfo(const RefPtr<AnimatorInfo>& animatorInfo, const RefPtr<Animator>& animator)
78 {
79 if (!animatorInfo || !animator) {
80 return;
81 }
82 int32_t duration = animatorInfo->GetDuration();
83 int32_t delay = animatorInfo->GetDelay();
84 FillMode fillMode = animatorInfo->GetFillMode();
85 int32_t iteration = animatorInfo->GetIteration();
86 AnimationDirection playMode = animatorInfo->GetPlayMode();
87 animator->SetDuration(duration);
88 animator->SetStartDelay(delay);
89 animator->SetFillMode(fillMode);
90 animator->SetIteration(iteration);
91 animator->SetAnimationDirection(playMode);
92 }
93
CreateAnimation(const RefPtr<AnimatorInfo> & animatorInfo,const RefPtr<Animator> & animator,AnimationStatus operation)94 bool CreateAnimation(
95 const RefPtr<AnimatorInfo>& animatorInfo, const RefPtr<Animator>& animator, AnimationStatus operation)
96 {
97 if (!animatorInfo || !animator) {
98 return false;
99 }
100 auto motion = animatorInfo->GetAnimatorMotion();
101 if (motion) {
102 auto frameEvent = animatorInfo->GetFrameEvent();
103 if (frameEvent) {
104 motion->AddListener([frameEvent](const float& progress) { frameEvent(progress); });
105 }
106 animator->ClearPauseListeners();
107 animator->ClearRepeatListeners();
108 animator->ClearIdleListeners();
109 if (operation == AnimationStatus::RUNNING && animator->GetStatus() != Animator::Status::RUNNING) {
110 animator->PlayMotion(motion);
111 } else if (operation == AnimationStatus::STOPPED) {
112 animator->Finish();
113 }
114 return false;
115 } else {
116 animator->ClearInterpolators();
117 auto keyframeBegin = AceType::MakeRefPtr<Keyframe<double>>(0.0, 0.0);
118 auto keyframeEnd = AceType::MakeRefPtr<Keyframe<double>>(1.0, 1.0);
119 auto keyframeAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
120 auto curve = animatorInfo->GetCurve();
121 if (curve) {
122 keyframeAnimation->SetCurve(curve);
123 }
124 keyframeAnimation->AddKeyframe(keyframeBegin);
125 keyframeAnimation->AddKeyframe(keyframeEnd);
126 AddFrameListener(animatorInfo, keyframeAnimation);
127 animator->AddInterpolator(keyframeAnimation);
128 return true;
129 }
130 }
131
GetEventCallback(const JSCallbackInfo & info,const std::string & name)132 std::function<void()> GetEventCallback(const JSCallbackInfo& info, const std::string& name)
133 {
134 if (!info[0]->IsFunction()) {
135 return nullptr;
136 }
137 RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
138 return [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), name]() {
139 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
140 ACE_SCORING_EVENT(name);
141 func->Execute();
142 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", name);
143 };
144 }
145
146 } // namespace
147
148 std::string JSAnimator::animatorId_;
149
JSBind(BindingTarget globalObj)150 void JSAnimator::JSBind(BindingTarget globalObj)
151 {
152 JSClass<JSAnimator>::Declare("Animator");
153 MethodOptions opt = MethodOptions::NONE;
154 JSClass<JSAnimator>::StaticMethod("create", &JSAnimator::Create, opt);
155 JSClass<JSAnimator>::StaticMethod("state", &JSAnimator::SetState, opt);
156 JSClass<JSAnimator>::StaticMethod("duration", &JSAnimator::SetDuration, opt);
157 JSClass<JSAnimator>::StaticMethod("curve", &JSAnimator::SetCurve, opt);
158 JSClass<JSAnimator>::StaticMethod("delay", &JSAnimator::SetDelay, opt);
159 JSClass<JSAnimator>::StaticMethod("fillMode", &JSAnimator::SetFillMode, opt);
160 JSClass<JSAnimator>::StaticMethod("iterations", &JSAnimator::SetIteration, opt);
161 JSClass<JSAnimator>::StaticMethod("playMode", &JSAnimator::SetPlayMode, opt);
162 JSClass<JSAnimator>::StaticMethod("motion", &JSAnimator::SetMotion, opt);
163 JSClass<JSAnimator>::StaticMethod("pop", &JSAnimator::Pop, opt);
164
165 JSClass<JSAnimator>::StaticMethod("onStart", &JSAnimator::OnStart, opt);
166 JSClass<JSAnimator>::StaticMethod("onPause", &JSAnimator::OnPause, opt);
167 JSClass<JSAnimator>::StaticMethod("onRepeat", &JSAnimator::OnRepeat, opt);
168 JSClass<JSAnimator>::StaticMethod("onCancel", &JSAnimator::OnCancel, opt);
169 JSClass<JSAnimator>::StaticMethod("onFinish", &JSAnimator::OnFinish, opt);
170 JSClass<JSAnimator>::StaticMethod("onFrame", &JSAnimator::OnFrame, opt);
171
172 JSClass<JSAnimator>::Bind<>(globalObj);
173
174 JSClass<JSSpringProp>::Declare("SpringProp");
175 JSClass<JSSpringProp>::Bind(globalObj, JSSpringProp::ConstructorCallback, JSSpringProp::DestructorCallback);
176
177 JSClass<JSMotion>::Declare("SpringMotion");
178 JSClass<JSMotion>::Bind(globalObj, JSMotion::ConstructorCallback, JSMotion::DestructorCallback);
179
180 JSClass<JSMotion>::Declare("FrictionMotion");
181 JSClass<JSMotion>::Bind(globalObj, JSMotion::ConstructorCallback, JSMotion::DestructorCallback);
182
183 JSClass<JSMotion>::Declare("ScrollMotion");
184 JSClass<JSMotion>::Bind(globalObj, JSMotion::ConstructorCallback, JSMotion::DestructorCallback);
185 }
186
Create(const JSCallbackInfo & info)187 void JSAnimator::Create(const JSCallbackInfo& info)
188 {
189 ContainerScope scope(Container::CurrentIdSafely());
190 if (info.Length() != 1) {
191 return;
192 }
193
194 if (!info[0]->IsString()) {
195 return;
196 }
197 animatorId_ = info[0]->ToString();
198 AnimatorModel::GetInstance()->Create(animatorId_);
199 }
200
Pop()201 void JSAnimator::Pop() {}
202
SetState(int32_t state)203 void JSAnimator::SetState(int32_t state)
204 {
205 ContainerScope scope(Container::CurrentIdSafely());
206 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
207 if (!animatorInfo) {
208 TAG_LOGW(AceLogTag::ACE_ANIMATION, "animator component setState failed, id:%{public}s, state:%{public}d",
209 animatorId_.c_str(), state);
210 return;
211 }
212 auto animator = animatorInfo->GetAnimator();
213 CHECK_NULL_VOID(animator);
214 auto operation = static_cast<AnimationStatus>(state);
215 HandleAnimatorInfo(animatorInfo, animator);
216 if (!CreateAnimation(animatorInfo, animator, operation)) {
217 return;
218 }
219 switch (operation) {
220 case AnimationStatus::RUNNING:
221 TAG_LOGI(AceLogTag::ACE_ANIMATION, "animator component play, id:%{public}s", animatorId_.c_str());
222 animator->Play();
223 break;
224 case AnimationStatus::PAUSED:
225 TAG_LOGI(AceLogTag::ACE_ANIMATION, "animator component pause, id:%{public}s", animatorId_.c_str());
226 animator->Pause();
227 break;
228 case AnimationStatus::STOPPED:
229 TAG_LOGI(AceLogTag::ACE_ANIMATION, "animator component stop, id:%{public}s", animatorId_.c_str());
230 animator->Finish();
231 break;
232 case AnimationStatus::INITIAL:
233 animator->Cancel();
234 break;
235 default:
236 break;
237 }
238 }
239
SetDuration(int32_t duration)240 void JSAnimator::SetDuration(int32_t duration)
241 {
242 ContainerScope scope(Container::CurrentIdSafely());
243 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
244 if (!animatorInfo) {
245 return;
246 }
247 animatorInfo->SetDuration(duration);
248 }
249
SetCurve(const JSCallbackInfo & info)250 void JSAnimator::SetCurve(const JSCallbackInfo& info)
251 {
252 ContainerScope scope(Container::CurrentIdSafely());
253 if (info.Length() != 1) {
254 return;
255 }
256
257 if (!info[0]->IsString()) {
258 return;
259 }
260 auto value = info[0]->ToString();
261 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
262 if (!animatorInfo) {
263 return;
264 }
265 auto curve = CreateCurve(value);
266 animatorInfo->SetCurve(curve);
267 }
268
SetDelay(int32_t delay)269 void JSAnimator::SetDelay(int32_t delay)
270 {
271 ContainerScope scope(Container::CurrentIdSafely());
272 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
273 if (!animatorInfo) {
274 return;
275 }
276 animatorInfo->SetDelay(delay);
277 }
278
SetFillMode(int32_t fillMode)279 void JSAnimator::SetFillMode(int32_t fillMode)
280 {
281 ContainerScope scope(Container::CurrentIdSafely());
282 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
283 if (!animatorInfo) {
284 return;
285 }
286 animatorInfo->SetFillMode(static_cast<FillMode>(fillMode));
287 }
288
SetIteration(int32_t iteration)289 void JSAnimator::SetIteration(int32_t iteration)
290 {
291 ContainerScope scope(Container::CurrentIdSafely());
292 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
293 if (!animatorInfo) {
294 return;
295 }
296 animatorInfo->SetIteration(iteration);
297 }
298
SetPlayMode(int32_t playMode)299 void JSAnimator::SetPlayMode(int32_t playMode)
300 {
301 ContainerScope scope(Container::CurrentIdSafely());
302 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
303 if (!animatorInfo) {
304 return;
305 }
306 animatorInfo->SetPlayMode(static_cast<AnimationDirection>(playMode));
307 }
308
SetMotion(const JSCallbackInfo & info)309 void JSAnimator::SetMotion(const JSCallbackInfo& info)
310 {
311 ContainerScope scope(Container::CurrentIdSafely());
312 if (info.Length() != 1 || !info[0]->IsObject()) {
313 return;
314 }
315 JSMotion* rawMotion = JSRef<JSObject>::Cast(info[0])->Unwrap<JSMotion>();
316 if (!rawMotion) {
317 return;
318 }
319
320 RefPtr<Motion> motion = rawMotion->GetMotion();
321 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
322 if (!animatorInfo) {
323 return;
324 }
325 animatorInfo->SetAnimatorMotion(motion);
326 }
327
OnStart(const JSCallbackInfo & info)328 void JSAnimator::OnStart(const JSCallbackInfo& info)
329 {
330 ContainerScope scope(Container::CurrentIdSafely());
331 auto callback = GetEventCallback(info, "Animator.onStart");
332 AnimatorModel::GetInstance()->AddEventListener(std::move(callback), EventOperation::START, animatorId_);
333 }
334
OnPause(const JSCallbackInfo & info)335 void JSAnimator::OnPause(const JSCallbackInfo& info)
336 {
337 ContainerScope scope(Container::CurrentIdSafely());
338 auto callback = GetEventCallback(info, "Animator.onPause");
339 AnimatorModel::GetInstance()->AddEventListener(std::move(callback), EventOperation::PAUSE, animatorId_);
340 }
341
OnRepeat(const JSCallbackInfo & info)342 void JSAnimator::OnRepeat(const JSCallbackInfo& info)
343 {
344 ContainerScope scope(Container::CurrentIdSafely());
345 auto callback = GetEventCallback(info, "Animator.onRepeat");
346 AnimatorModel::GetInstance()->AddEventListener(std::move(callback), EventOperation::REPEAT, animatorId_);
347 }
348
OnCancel(const JSCallbackInfo & info)349 void JSAnimator::OnCancel(const JSCallbackInfo& info)
350 {
351 ContainerScope scope(Container::CurrentIdSafely());
352 auto callback = GetEventCallback(info, "Animator.onCancel");
353 AnimatorModel::GetInstance()->AddEventListener(std::move(callback), EventOperation::CANCEL, animatorId_);
354 }
355
OnFinish(const JSCallbackInfo & info)356 void JSAnimator::OnFinish(const JSCallbackInfo& info)
357 {
358 ContainerScope scope(Container::CurrentIdSafely());
359 auto callback = GetEventCallback(info, "Animator.onFinish");
360 AnimatorModel::GetInstance()->AddEventListener(std::move(callback), EventOperation::FINISH, animatorId_);
361 }
362
OnFrame(const JSCallbackInfo & info)363 void JSAnimator::OnFrame(const JSCallbackInfo& info)
364 {
365 ContainerScope scope(Container::CurrentIdSafely());
366 if (!info[0]->IsFunction()) {
367 return;
368 }
369 RefPtr<JsAnimatorFunction> function = AceType::MakeRefPtr<JsAnimatorFunction>(JSRef<JSFunc>::Cast(info[0]));
370 auto OnFrameEvent = [execCtx = info.GetExecutionContext(), func = std::move(function)](const float& progress) {
371 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
372 ACE_SCORING_EVENT("Animator.onFrame");
373 func->Execute(progress);
374 };
375 auto animatorInfo = AnimatorModel::GetInstance()->GetAnimatorInfo(animatorId_);
376 if (!animatorInfo) {
377 return;
378 }
379 animatorInfo->SetFrameEvent(OnFrameEvent);
380 }
381
ConstructorCallback(const JSCallbackInfo & info)382 void JSSpringProp::ConstructorCallback(const JSCallbackInfo& info)
383 {
384 ContainerScope scope(Container::CurrentIdSafely());
385 if (info.Length() != 3 || !info[0]->IsNumber() || !info[1]->IsNumber() || !info[2]->IsNumber()) {
386 return;
387 }
388 auto obj = AceType::MakeRefPtr<JSSpringProp>();
389 double mass = info[0]->ToNumber<double>();
390 double stiffness = info[1]->ToNumber<double>();
391 double damping = info[2]->ToNumber<double>();
392 auto springProp = AceType::MakeRefPtr<SpringProperty>(mass, stiffness, damping);
393 obj->SetSpringProp(springProp);
394 obj->IncRefCount();
395 info.SetReturnValue(AceType::RawPtr(obj));
396 }
397
DestructorCallback(JSSpringProp * obj)398 void JSSpringProp::DestructorCallback(JSSpringProp* obj)
399 {
400 if (obj != nullptr) {
401 obj->DecRefCount();
402 }
403 }
404
ConstructorCallback(const JSCallbackInfo & info)405 void JSMotion::ConstructorCallback(const JSCallbackInfo& info)
406 {
407 ContainerScope scope(Container::CurrentIdSafely());
408 int32_t len = static_cast<int32_t>(info.Length());
409 if (len != FRICTION_MOTION_LENGTH && len != SPRING_MOTION_LENGTH && len != SCROLL_MOTION_LENGTH) {
410 return;
411 }
412 auto obj = AceType::MakeRefPtr<JSMotion>();
413 if (len == FRICTION_MOTION_LENGTH) {
414 HandleFrictionMotion(info, obj);
415 } else if (len == SPRING_MOTION_LENGTH) {
416 HandleSpringMotion(info, obj);
417 } else {
418 HandleScrollMotion(info, obj);
419 }
420 obj->IncRefCount();
421 info.SetReturnValue(AceType::RawPtr(obj));
422 }
423
HandleFrictionMotion(const JSCallbackInfo & info,RefPtr<JSMotion> & obj)424 void JSMotion::HandleFrictionMotion(const JSCallbackInfo& info, RefPtr<JSMotion>& obj)
425 {
426 if (!info[0]->IsNumber() || !info[1]->IsNumber() || !info[2]->IsNumber()) {
427 return;
428 }
429 double friction = info[0]->ToNumber<double>();
430 double position = info[1]->ToNumber<double>();
431 double velocity = info[2]->ToNumber<double>();
432 RefPtr<FrictionMotion> frictionMotion = AceType::MakeRefPtr<FrictionMotion>(friction, position, velocity);
433 obj->SetMotion(frictionMotion);
434 }
435
HandleSpringMotion(const JSCallbackInfo & info,RefPtr<JSMotion> & obj)436 void JSMotion::HandleSpringMotion(const JSCallbackInfo& info, RefPtr<JSMotion>& obj)
437 {
438 if (!info[0]->IsNumber() || !info[1]->IsNumber() || !info[2]->IsNumber() || !info[3]->IsObject()) {
439 return;
440 }
441 double start = info[0]->ToNumber<double>();
442 double end = info[1]->ToNumber<double>();
443 double velocity = info[2]->ToNumber<double>();
444 JSSpringProp* prop = JSRef<JSObject>::Cast(info[3])->Unwrap<JSSpringProp>();
445 if (!prop) {
446 return;
447 }
448 RefPtr<SpringProperty> springProperty = prop->GetSpringProp();
449 auto springMotion = AceType::MakeRefPtr<SpringMotion>(start, end, velocity, springProperty);
450 obj->SetMotion(springMotion);
451 }
452
HandleScrollMotion(const JSCallbackInfo & info,RefPtr<JSMotion> & obj)453 void JSMotion::HandleScrollMotion(const JSCallbackInfo& info, RefPtr<JSMotion>& obj)
454 {
455 if (!info[0]->IsNumber() || !info[1]->IsNumber() || !info[2]->IsNumber() || !info[3]->IsNumber() ||
456 !info[4]->IsObject()) {
457 return;
458 }
459 double position = info[0]->ToNumber<double>();
460 double velocity = info[1]->ToNumber<double>();
461 double min = info[2]->ToNumber<double>();
462 double max = info[3]->ToNumber<double>();
463 JSSpringProp* prop = JSRef<JSObject>::Cast(info[4])->Unwrap<JSSpringProp>();
464 if (!prop) {
465 return;
466 }
467 RefPtr<SpringProperty> springProperty = prop->GetSpringProp();
468 RefPtr<ScrollMotion> scrollMotion = AceType::MakeRefPtr<ScrollMotion>(
469 position, velocity, ExtentPair(min, min), ExtentPair(max, max), springProperty);
470 obj->SetMotion(scrollMotion);
471 }
472
DestructorCallback(JSMotion * obj)473 void JSMotion::DestructorCallback(JSMotion* obj)
474 {
475 if (obj != nullptr) {
476 obj->DecRefCount();
477 }
478 }
479
480 } // namespace OHOS::Ace::Framework
481