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