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