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 "core/components/image/image_animator_element.h"
17
18 #include "base/utils/string_utils.h"
19 #include "core/event/ace_event_helper.h"
20
21 namespace OHOS::Ace {
22
~ImageAnimatorElement()23 ImageAnimatorElement::~ImageAnimatorElement()
24 {
25 auto pageElement = pageElement_.Upgrade();
26 if (pageElement && callbackId_ >= 0) {
27 pageElement->CancelHiddenCallback(callbackId_);
28 }
29 }
30
Update()31 void Ace::ImageAnimatorElement::Update()
32 {
33 const auto imageAnimatorComponent = AceType::DynamicCast<ImageAnimatorComponent>(component_);
34 if (!imageAnimatorComponent) {
35 LOGE("ImageAnimator element update failed. imageAnimatorComponent is null.");
36 return;
37 }
38 SetElementId(imageAnimatorComponent->GetElementId());
39
40 if (!animator_) {
41 animator_ = CREATE_ANIMATOR();
42 }
43 UpdateCallbackAndFunc(imageAnimatorComponent);
44 if (!animator_->HasScheduler()) {
45 animator_->AttachScheduler(context_);
46 }
47 animator_->SetFillMode(imageAnimatorComponent->GetFillMode());
48 animator_->SetIteration(imageAnimatorComponent->GetIteration());
49 status_ = imageAnimatorComponent->GetStatus();
50 preDecode_ = imageAnimatorComponent->GetPreDecode();
51 duration_ = imageAnimatorComponent->GetDuration();
52 isReverse_ = imageAnimatorComponent->GetIsReverse();
53 images_ = imageAnimatorComponent->GetImageProperties();
54 isFixedSize_ = imageAnimatorComponent->GetIsFixedSize();
55 border_ = imageAnimatorComponent->GetBorder();
56 fillMode_ = imageAnimatorComponent->GetFillMode();
57 iteration_ = imageAnimatorComponent->GetIteration();
58 UpdateFilterImages();
59
60 if (!pageElement_.Invalid()) {
61 return;
62 }
63
64 auto pageElement = GetPageElement();
65 if (!pageElement) {
66 return;
67 }
68 pageElement_ = pageElement;
69 callbackId_ = pageElement->RegisterHiddenCallback([weak = AceType::WeakClaim(this)](bool hidden) {
70 auto element = weak.Upgrade();
71 if (!element || !element->animator_) {
72 return;
73 }
74
75 if (hidden) {
76 if (element->animator_->GetStatus() == Animator::Status::RUNNING) {
77 element->animator_->Pause();
78 element->isPaused_ = true;
79 }
80 } else {
81 if (element->isPaused_) {
82 if (element->isReverse_) {
83 element->animator_->Backward();
84 } else {
85 element->animator_->Forward();
86 }
87 element->isPaused_ = false;
88 }
89 }
90 });
91 }
92
PerformBuild()93 void ImageAnimatorElement::PerformBuild()
94 {
95 int32_t size = static_cast<int32_t>(images_.size());
96 if (size <= 0) {
97 LOGE("image size is less than 0.");
98 return;
99 }
100 if (children_.empty()) {
101 // first time to set image child.
102 childComponent_ = BuildChild();
103 ComposedElement::PerformBuild();
104 }
105 if (preDecode_ > 1) {
106 auto boxComponent = DynamicCast<BoxComponent>(childComponent_);
107 UpdatePreLoadImages(boxComponent);
108 }
109 CreatePictureAnimation(size);
110 if (!animator_) {
111 LOGE("animator is null, need to get animator first.");
112 return;
113 }
114 // update duration after a loop of animation.
115 if (!isSetDuration_) {
116 durationTotal_ > 0 ? animator_->SetDuration(durationTotal_) : animator_->SetDuration(duration_);
117 isSetDuration_ = true;
118 }
119 animator_->ClearInterpolators();
120 animator_->AddInterpolator(pictureAnimation_);
121 animator_->RemoveRepeatListener(repeatCallbackId_);
122 repeatCallbackId_ = static_cast<int64_t>(animator_->AddRepeatListener([weak = WeakClaim(this)]() {
123 auto imageAnimator = weak.Upgrade();
124 if (!imageAnimator) {
125 return;
126 }
127 if (imageAnimator->durationTotal_ > 0) {
128 imageAnimator->animator_->SetDuration(imageAnimator->durationTotal_);
129 } else {
130 imageAnimator->animator_->SetDuration(imageAnimator->duration_);
131 }
132 }));
133 animator_->RemoveStopListener(stopCallbackId_);
134 stopCallbackId_ = static_cast<int64_t>(animator_->AddStopListener([weak = WeakClaim(this)]() {
135 auto imageAnimator = weak.Upgrade();
136 if (imageAnimator) {
137 imageAnimator->isSetDuration_ = false;
138 }
139 }));
140
141 // for declarative frontend.
142 if (context_.Upgrade() && context_.Upgrade()->GetIsDeclarative()) {
143 if (status_ == Animator::Status::IDLE) {
144 CallAnimatorMethod(CANCEL);
145 } else if (status_ == Animator::Status::PAUSED) {
146 CallAnimatorMethod(PAUSE);
147 } else if (status_ == Animator::Status::STOPPED) {
148 CallAnimatorMethod(STOP);
149 } else {
150 CallAnimatorMethod(START);
151 }
152 return;
153 }
154 isReverse_ ? animator_->Backward() : animator_->Forward();
155 }
156
BuildChild()157 RefPtr<Component> ImageAnimatorElement::BuildChild()
158 {
159 uint32_t size = images_.size();
160 if (size == 0) {
161 LOGE("image size is 0.");
162 return nullptr;
163 }
164 auto boxComponent = AceType::MakeRefPtr<BoxComponent>();
165 boxComponent->SetFlex(BoxFlex::FLEX_XY);
166 boxComponent->SetAlignment(Alignment::TOP_LEFT);
167 ImageProperties childImage;
168 if (durationTotal_ > 0) {
169 childImage = (isReverse_ ? filterImages_.back() : filterImages_.front());
170 } else {
171 childImage = (isReverse_ ? images_.back() : images_.front());
172 }
173 auto imageComponent = AceType::MakeRefPtr<ImageComponent>(childImage.src);
174 if (!isFixedSize_) {
175 UpdateImageSize(childImage, imageComponent);
176 }
177 if (!childImage.src.empty()) {
178 imageComponent->SetBorder(border_);
179 imageComponent->SetFitMaxSize(true);
180 }
181 boxComponent->SetChild(imageComponent);
182 return boxComponent;
183 }
184
UpdatePreLoadImages(const RefPtr<BoxComponent> & box)185 void ImageAnimatorElement::UpdatePreLoadImages(const RefPtr<BoxComponent>& box)
186 {
187 if (!box) {
188 LOGE("boxComponent is null.");
189 return;
190 }
191 int32_t size = static_cast<int32_t>(images_.size());
192 for (int32_t idx = 0; (idx < preDecode_) && (idx < size); idx++) {
193 auto imageComponent = DynamicCast<ImageComponent>(box->GetChild());
194 if (!imageComponent) {
195 LOGE("imageComponent is null.");
196 return;
197 }
198 ImageProperties childImage = images_[idx];
199 if (!childImage.src.empty()) {
200 imageComponent->SetSrc(childImage.src);
201 }
202 }
203 }
204
CreatePictureAnimation(int32_t size)205 void ImageAnimatorElement::CreatePictureAnimation(int32_t size)
206 {
207 if (!pictureAnimation_) {
208 pictureAnimation_ = MakeRefPtr<PictureAnimation<int32_t>>();
209 }
210
211 pictureAnimation_->ClearListeners();
212 pictureAnimation_->ClearPictures();
213 if (durationTotal_ > 0) {
214 int32_t filterImagesSize = static_cast<int32_t>(filterImages_.size());
215 for (int32_t index = 0; index < filterImagesSize; ++index) {
216 int32_t imageDuration = filterImages_[index].duration;
217 pictureAnimation_->AddPicture((float)imageDuration / durationTotal_, index);
218 }
219 } else {
220 for (int32_t index = 0; index < size; ++index) {
221 pictureAnimation_->AddPicture(NORMALIZED_DURATION_MAX / size, index);
222 }
223 }
224 pictureAnimation_->AddListener([weak = WeakClaim(this)](const int32_t index) {
225 auto imageAnimator = weak.Upgrade();
226 if (imageAnimator) {
227 imageAnimator->PlayImageAnimator(index);
228 }
229 });
230 }
231
UpdateFilterImages()232 void ImageAnimatorElement::UpdateFilterImages()
233 {
234 filterImages_.clear();
235 durationTotal_ = 0;
236 for (auto& childImage : images_) {
237 if (!childImage.src.empty() && childImage.duration > 0) {
238 durationTotal_ += childImage.duration;
239 filterImages_.emplace_back(childImage);
240 }
241 }
242 }
243
PlayImageAnimator(int32_t index)244 void ImageAnimatorElement::PlayImageAnimator(int32_t index)
245 {
246 auto boxComponent = DynamicCast<BoxComponent>(childComponent_);
247 if (!boxComponent) {
248 LOGE("child boxComponent is null.");
249 return;
250 }
251 auto imageComponent = DynamicCast<ImageComponent>(boxComponent->GetChild());
252 if (!imageComponent) {
253 LOGE("imageComponent is null.");
254 return;
255 }
256 ImageProperties childImage;
257 if (durationTotal_ > 0) {
258 childImage = filterImages_[index];
259 } else {
260 childImage = images_[index];
261 }
262 if (!isFixedSize_) {
263 UpdateImageSize(childImage, imageComponent);
264 isResetBox_ = false;
265 } else {
266 if (!isResetBox_) {
267 imageComponent->SetLeft(Dimension());
268 imageComponent->SetTop(Dimension());
269 // Follows the size of the parent component
270 imageComponent->SetWidth(-1.0);
271 imageComponent->SetHeight(-1.0);
272 isResetBox_ = true;
273 }
274 }
275 if (!childImage.src.empty()) {
276 imageComponent->SetSrc(childImage.src);
277 }
278 UpdateChild(GetFirstChild(), boxComponent);
279 }
280
UpdateImageSize(ImageProperties & imageProperties,const RefPtr<ImageComponent> & image)281 void ImageAnimatorElement::UpdateImageSize(ImageProperties& imageProperties, const RefPtr<ImageComponent>& image)
282 {
283 image->SetLeft(imageProperties.left);
284 image->SetTop(imageProperties.top);
285 if (imageProperties.width.IsValid()) {
286 image->SetWidth(imageProperties.width);
287 }
288 if (imageProperties.height.IsValid()) {
289 image->SetHeight(imageProperties.height);
290 }
291 }
292
CallAnimatorMethod(const std::string & method)293 void ImageAnimatorElement::CallAnimatorMethod(const std::string& method)
294 {
295 if (!animator_) {
296 LOGE("CallAnimatorMethod failed, animator is null.");
297 return;
298 }
299 if (method == START) {
300 isReverse_ ? animator_->Backward() : animator_->Forward();
301 } else if (method == PAUSE) {
302 animator_->Pause();
303 } else if (method == STOP) {
304 animator_->Finish();
305 } else if (method == RESUME) {
306 animator_->Resume();
307 } else if (method == CANCEL) {
308 animator_->Cancel();
309 } else {
310 LOGE("Unsupported method name : %s", method.c_str());
311 }
312 }
313
UpdateCallbackAndFunc(const RefPtr<ImageAnimatorComponent> & imageAnimatorComponent)314 void ImageAnimatorElement::UpdateCallbackAndFunc(const RefPtr<ImageAnimatorComponent>& imageAnimatorComponent)
315 {
316 const auto& imageAnimatorController = imageAnimatorComponent->GetImageAnimatorController();
317 if (!imageAnimatorController) {
318 LOGE("UpdateCallbackAndFunc failed, imageAnimatorController is null.");
319 return;
320 }
321
322 // start / stop / pause / resume method.
323 imageAnimatorController->SetAnimationFunc([weak = WeakClaim(this)](const std::string& method) {
324 auto element = weak.Upgrade();
325 if (element) {
326 element->CallAnimatorMethod(method);
327 }
328 });
329
330 // getStatus method.
331 imageAnimatorController->SetAnimatorGetStatusFunc([weak = WeakClaim(this)]() -> Animator::Status {
332 auto element = weak.Upgrade();
333 if (element) {
334 return element->GetAnimatorStatus();
335 }
336 return Animator::Status::IDLE;
337 });
338
339 animator_->ClearAllListeners();
340 auto startEvent = imageAnimatorController->GetStartEvent();
341 if (!startEvent.IsEmpty()) {
342 animator_->AddStartListener(
343 [startEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(startEvent, weakContext)(); });
344 }
345 auto stopEvent = imageAnimatorController->GetStopEvent();
346 if (!stopEvent.IsEmpty()) {
347 animator_->AddStopListener(
348 [stopEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(stopEvent, weakContext)(); });
349 }
350 auto pauseEvent = imageAnimatorController->GetPauseEvent();
351 if (!pauseEvent.IsEmpty()) {
352 animator_->AddPauseListener(
353 [pauseEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(pauseEvent, weakContext)(); });
354 }
355 auto resumeEvent = imageAnimatorController->GetResumeEvent();
356 if (!resumeEvent.IsEmpty()) {
357 animator_->AddResumeListener(
358 [resumeEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(resumeEvent, weakContext)(); });
359 }
360 auto repeatEvent = imageAnimatorController->GetRepeatEvent();
361 if (!repeatEvent.IsEmpty()) {
362 animator_->AddRepeatListener(
363 [repeatEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(repeatEvent, weakContext)(); });
364 }
365 auto cancelEvent = imageAnimatorController->GetCancelEvent();
366 if (!cancelEvent.IsEmpty()) {
367 animator_->AddIdleListener(
368 [cancelEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(cancelEvent, weakContext)(); });
369 }
370 }
371
GetAnimatorStatus() const372 Animator::Status ImageAnimatorElement::GetAnimatorStatus() const
373 {
374 if (animator_) {
375 return animator_->GetStatus();
376 }
377 return Animator::Status::IDLE;
378 }
379
CanUpdate(const RefPtr<Component> & newComponent)380 bool ImageAnimatorElement::CanUpdate(const RefPtr<Component>& newComponent)
381 {
382 return Element::CanUpdate(newComponent);
383 }
384
385 } // namespace OHOS::Ace
386