• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 #include "AnimationJS.h"
16 
17 #include <meta/api/make_callback.h>
18 #include <meta/interface/animation/intf_animation.h>
19 #include <meta/interface/animation/builtin_animations.h>
20 #include <meta/interface/intf_attach.h>
21 #include <scene/interface/intf_scene.h>
22 #include <scene/ext/intf_ecs_object_access.h>
23 
24 #include "SceneJS.h"
25 
26 class OnCallJS : public ThreadSafeCallback {
27     NapiApi::StrongRef jsThis_;
28     NapiApi::StrongRef ref_;
29 
30 public:
OnCallJS(const char * name,NapiApi::Object jsThis,NapiApi::Function toCall)31     OnCallJS(const char* name, NapiApi::Object jsThis, NapiApi::Function toCall)
32         : ThreadSafeCallback(toCall.GetEnv(), name), jsThis_(jsThis), ref_(toCall.GetEnv(), toCall)
33     {}
~OnCallJS()34     ~OnCallJS()
35     {
36         jsThis_.Reset();
37         ref_.Reset();
38         Release();
39     }
Finalize(napi_env env)40     void Finalize(napi_env env)
41     {
42         jsThis_.Reset();
43         ref_.Reset();
44     }
Invoked(napi_env env)45     void Invoked(napi_env env)
46     {
47         napi_value res;
48         napi_call_function(env, jsThis_.GetValue(), ref_.GetValue(), 0, nullptr, &res);
49     }
50 };
51 
Init(napi_env env,napi_value exports)52 void AnimationJS::Init(napi_env env, napi_value exports)
53 {
54     BASE_NS::vector<napi_property_descriptor> node_props;
55     SceneResourceImpl::GetPropertyDescs(node_props);
56 // Try out the helper macros.
57 // Declare NAPI_API_JS_NAME to simplify the registering.
58 #define NAPI_API_JS_NAME Animation
59 
60     DeclareGetSet(bool, "enabled", GetEnabled, SetEnabled);
61     DeclareGetSet(float, "speed", GetSpeed, SetSpeed);
62     DeclareGet(float, "duration", GetDuration);
63     DeclareGet(bool, "running", GetRunning);
64     DeclareGet(float, "progress", GetProgress);
65     DeclareMethod("pause", Pause);
66     DeclareMethod("restart", Restart);
67     DeclareMethod("seek", Seek, float);
68     DeclareMethod("start", Start);
69     DeclareMethod("stop", Stop);
70     DeclareMethod("finish", Finish);
71     DeclareMethod("onFinished", OnFinished, NapiApi::Function);
72     DeclareMethod("onStarted", OnStarted, NapiApi::Function);
73 
74     DeclareClass();
75 #undef NAPI_API_JS_NAME
76 }
77 
AnimationJS(napi_env e,napi_callback_info i)78 AnimationJS::AnimationJS(napi_env e, napi_callback_info i)
79     : BaseObject(e, i), SceneResourceImpl(SceneResourceImpl::ANIMATION)
80 {
81     NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
82     NapiApi::Object meJs(fromJs.This());
83     NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene...
84     NapiApi::Object args = fromJs.Arg<1>();  // access to params.
85     scene_ = { scene };
86     if (!scene_.GetObject().GetNative<SCENE_NS::IScene>()) {
87         LOG_F("INVALID SCENE!");
88     }
89 
90     if (const auto sceneJS = scene_.GetObject().GetJsWrapper<SceneJS>()) {
91         sceneJS->DisposeHook(reinterpret_cast<uintptr_t>(&scene_), meJs);
92     }
93 
94     META_NS::IAnimation::Ptr anim;
95     if (args) {
96         using namespace META_NS;
97         anim = interface_pointer_cast<IAnimation>(GetNativeObject());
98         if (anim) {
99             // check if there is a speed controller already.(and use that)
100             auto attachments = interface_cast<META_NS::IAttach>(anim)->GetAttachments();
101             for (auto at : attachments) {
102                 if (interface_cast<AnimationModifiers::ISpeed>(at)) {
103                     // yes.. (expect at most one)
104                     speedModifier_ = interface_pointer_cast<AnimationModifiers::ISpeed>(at);
105                     break;
106                 }
107             }
108         }
109     }
110     if (anim) {
111 #if defined(USE_ANIMATION_STATE_COMPONENT_ON_COMPLETED) && (USE_ANIMATION_STATE_COMPONENT_ON_COMPLETED == 1)
112         using namespace SCENE_NS;
113         auto acc = interface_cast<IEcsObjectAccess>(anim);
114         IEcsObject::Ptr ecsObj;
115         if ((acc) && (ecsObj = acc->GetEcsObject())) {
116             completed_ = ecsObj->CreateProperty("AnimationStateComponent.completed").GetResult();
117             if (completed_) {
118                 OnCompletedEvent_ = completed_->OnChanged();
119             }
120         }
121 #else
122         // Use IAnimation OnFinished to trigger the animation ends (has a bug)
123         OnCompletedEvent_ = anim->OnFinished();
124 #endif
125     }
126 }
GetInstanceImpl(uint32_t id)127 void* AnimationJS::GetInstanceImpl(uint32_t id)
128 {
129     if (id == AnimationJS::ID)
130         return this;
131     return SceneResourceImpl::GetInstanceImpl(id);
132 }
133 
Finalize(napi_env env)134 void AnimationJS::Finalize(napi_env env)
135 {
136     DisposeNative(scene_.GetObject().GetJsWrapper<SceneJS>());
137     BaseObject::Finalize(env);
138 }
~AnimationJS()139 AnimationJS::~AnimationJS()
140 {
141     LOG_V("AnimationJS -- ");
142     DisposeNative(nullptr);
143 }
144 
DisposeNative(void *)145 void AnimationJS::DisposeNative(void*)
146 {
147     // do nothing for now..
148     if (!disposed_) {
149         disposed_ = true;
150 
151         LOG_V("AnimationJS::DisposeNative");
152         if (const auto sceneJS = scene_.GetObject().GetJsWrapper<SceneJS>()) {
153             sceneJS->ReleaseDispose(reinterpret_cast<uintptr_t>(&scene_));
154         }
155         scene_.Reset();
156 
157         // release all the native resources (callbacks, modifiers, properties)
158         if (OnCompletedEvent_ && OnFinishedToken_) {
159             OnCompletedEvent_->RemoveHandler(OnFinishedToken_);
160         }
161         OnCompletedEvent_ = {};
162         OnFinishedToken_ = 0;
163 #if defined(USE_ANIMATION_STATE_COMPONENT_ON_COMPLETED) && (USE_ANIMATION_STATE_COMPONENT_ON_COMPLETED == 1)
164         completed_.reset();
165 #endif
166         if (auto anim = interface_pointer_cast<META_NS::IAnimation>(GetNativeObject())) {
167             UnsetNativeObject();
168             // remove listeners.
169             if (OnStartedToken_) {
170                 anim->OnStarted()->RemoveHandler(OnStartedToken_);
171             }
172             OnStartedToken_ = 0;
173             if (speedModifier_) {
174                 auto attach = interface_cast<META_NS::IAttach>(anim);
175                 if (attach) {
176                     attach->Detach(speedModifier_);
177                 }
178                 speedModifier_.reset();
179             }
180             if (OnStartedCB_) {
181                 // does a delayed delete
182                 OnStartedCB_->Release();
183                 OnStartedCB_ = nullptr;
184             }
185             if (OnFinishedCB_) {
186                 // does a delayed delete
187                 OnFinishedCB_->Release();
188                 OnFinishedCB_ = nullptr;
189             }
190         }
191     }
192 }
GetSpeed(NapiApi::FunctionContext<> & ctx)193 napi_value AnimationJS::GetSpeed(NapiApi::FunctionContext<>& ctx)
194 {
195     if (!validateSceneRef()) {
196         return ctx.GetUndefined();
197     }
198 
199     float speed = 1.0;
200     if (speedModifier_) {
201         speed = speedModifier_->SpeedFactor()->GetValue();
202     }
203 
204     return ctx.GetNumber(speed);
205 }
206 
SetSpeed(NapiApi::FunctionContext<float> & ctx)207 void AnimationJS::SetSpeed(NapiApi::FunctionContext<float>& ctx)
208 {
209     if (!validateSceneRef()) {
210         return;
211     }
212     float speed = ctx.Arg<0>();
213     if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
214         using namespace META_NS;
215         if (!speedModifier_) {
216             speedModifier_ =
217                 GetObjectRegistry().Create<AnimationModifiers::ISpeed>(ClassId::SpeedAnimationModifier);
218             interface_cast<IAttach>(a)->Attach(speedModifier_);
219         }
220         speedModifier_->SpeedFactor()->SetValue(speed);
221     }
222 }
223 
GetEnabled(NapiApi::FunctionContext<> & ctx)224 napi_value AnimationJS::GetEnabled(NapiApi::FunctionContext<>& ctx)
225 {
226     if (!validateSceneRef()) {
227         return ctx.GetUndefined();
228     }
229 
230     bool enabled { false };
231     if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
232         enabled = a->Enabled()->GetValue();
233     }
234     return ctx.GetBoolean(enabled);
235 }
SetEnabled(NapiApi::FunctionContext<bool> & ctx)236 void AnimationJS::SetEnabled(NapiApi::FunctionContext<bool>& ctx)
237 {
238     if (!validateSceneRef()) {
239         return;
240     }
241     bool enabled = ctx.Arg<0>();
242     if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
243         a->Enabled()->SetValue(enabled);
244     }
245 }
GetDuration(NapiApi::FunctionContext<> & ctx)246 napi_value AnimationJS::GetDuration(NapiApi::FunctionContext<>& ctx)
247 {
248     if (!validateSceneRef()) {
249         return ctx.GetUndefined();
250     }
251     float duration = 0.0;
252     if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
253         duration = a->TotalDuration()->GetValue().ToSecondsFloat();
254     }
255 
256     return ctx.GetNumber(duration);
257 }
258 
GetRunning(NapiApi::FunctionContext<> & ctx)259 napi_value AnimationJS::GetRunning(NapiApi::FunctionContext<>& ctx)
260 {
261     if (!validateSceneRef()) {
262         return ctx.GetUndefined();
263     }
264     bool running { false };
265     if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
266         running = a->Running()->GetValue();
267     }
268     return ctx.GetBoolean(running);
269 }
GetProgress(NapiApi::FunctionContext<> & ctx)270 napi_value AnimationJS::GetProgress(NapiApi::FunctionContext<>& ctx)
271 {
272     if (!validateSceneRef()) {
273         return ctx.GetUndefined();
274     }
275     float progress = 0.0;
276     if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
277         progress = a->Progress()->GetValue();
278     }
279     return ctx.GetNumber(progress);
280 }
281 
OnFinished(NapiApi::FunctionContext<NapiApi::Function> & ctx)282 napi_value AnimationJS::OnFinished(NapiApi::FunctionContext<NapiApi::Function>& ctx)
283 {
284     auto func = ctx.Arg<0>();
285     if (!OnCompletedEvent_) {
286         return ctx.GetUndefined();
287     }
288     // do we have existing callback?
289     if (OnFinishedCB_) {
290         // stop listening ...
291         OnCompletedEvent_->RemoveHandler(OnFinishedToken_);
292         OnFinishedToken_ = 0;
293         // ... and release it
294         OnFinishedCB_->Release();
295         OnFinishedCB_ = nullptr;
296     }
297     if (!validateSceneRef()) {
298         return ctx.GetUndefined();
299     }
300 
301     // do we have a new callback?
302     if (func.IsDefinedAndNotNull()) {
303         // create handler...
304         OnFinishedCB_ = new OnCallJS("OnFinished", ctx.This(), func);
305         // ... and start listening
306 #if defined(USE_ANIMATION_STATE_COMPONENT_ON_COMPLETED) && (USE_ANIMATION_STATE_COMPONENT_ON_COMPLETED == 1)
307         auto cb = META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
308             bool completion = false;
309             if (!completed_) {
310                 return;
311             }
312             completed_->GetValue().GetValue(completion);
313             if (completion && OnFinishedCB_) {
314                 OnFinishedCB_->Trigger();
315             }
316         });
317 #else
318         auto cb = META_NS::MakeCallback<META_NS::IOnChanged>(OnFinishedCB_, &OnCallJS::Trigger);
319 #endif
320         OnFinishedToken_ = OnCompletedEvent_->AddHandler(cb);
321     }
322     return ctx.GetUndefined();
323 }
324 
OnStarted(NapiApi::FunctionContext<NapiApi::Function> & ctx)325 napi_value AnimationJS::OnStarted(NapiApi::FunctionContext<NapiApi::Function>& ctx)
326 {
327     auto func = ctx.Arg<0>();
328     // do we have existing callback?
329     if (OnStartedCB_) {
330         // stop listening ...
331         if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
332             a->OnStarted()->RemoveHandler(OnStartedToken_);
333             OnStartedToken_ = 0;
334         }
335         // ... and release it
336         OnStartedCB_->Release();
337         OnStartedCB_ = nullptr;
338     }
339     if (!validateSceneRef()) {
340         return ctx.GetUndefined();
341     }
342     // do we have a new callback?
343     if (func.IsDefinedAndNotNull()) {
344         // create handler...
345         OnStartedCB_ = new OnCallJS("OnStart", ctx.This(), func);
346         // ... and start listening
347         if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
348             OnStartedToken_ = a->OnStarted()->AddHandler(
349                 META_NS::MakeCallback<META_NS::IOnChanged>(OnStartedCB_, &OnCallJS::Trigger));
350         }
351     }
352     return ctx.GetUndefined();
353 }
354 
Pause(NapiApi::FunctionContext<> & ctx)355 napi_value AnimationJS::Pause(NapiApi::FunctionContext<>& ctx)
356 {
357     if (!validateSceneRef()) {
358         return ctx.GetUndefined();
359     }
360 
361     if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
362         a->Pause();
363     }
364     return ctx.GetUndefined();
365 }
Restart(NapiApi::FunctionContext<> & ctx)366 napi_value AnimationJS::Restart(NapiApi::FunctionContext<>& ctx)
367 {
368     if (!validateSceneRef()) {
369         return ctx.GetUndefined();
370     }
371     if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
372         a->Restart();
373     }
374     return ctx.GetUndefined();
375 }
Seek(NapiApi::FunctionContext<float> & ctx)376 napi_value AnimationJS::Seek(NapiApi::FunctionContext<float>& ctx)
377 {
378     if (!validateSceneRef()) {
379         return ctx.GetUndefined();
380     }
381     float pos = ctx.Arg<0>();
382     if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
383         a->Seek(pos);
384     }
385     return ctx.GetUndefined();
386 }
Start(NapiApi::FunctionContext<> & ctx)387 napi_value AnimationJS::Start(NapiApi::FunctionContext<>& ctx)
388 {
389     if (!validateSceneRef()) {
390         return ctx.GetUndefined();
391     }
392     if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
393         a->Start();
394     }
395     return ctx.GetUndefined();
396 }
397 
Stop(NapiApi::FunctionContext<> & ctx)398 napi_value AnimationJS::Stop(NapiApi::FunctionContext<>& ctx)
399 {
400     if (!validateSceneRef()) {
401         return ctx.GetUndefined();
402     }
403     if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
404         a->Stop();
405     }
406     return ctx.GetUndefined();
407 }
Finish(NapiApi::FunctionContext<> & ctx)408 napi_value AnimationJS::Finish(NapiApi::FunctionContext<>& ctx)
409 {
410     if (!validateSceneRef()) {
411         return ctx.GetUndefined();
412     }
413     if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
414         a->Finish();
415     }
416     return ctx.GetUndefined();
417 }
418