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 "staggered_animation_state.h"
16
17 META_BEGIN_NAMESPACE()
18
19 namespace Internal {
20
Initialize(AnimationStateParams && params)21 bool StaggeredAnimationState::Initialize(AnimationStateParams&& params)
22 {
23 if (!Super::Initialize(BASE_NS::move(params))) {
24 return false;
25 }
26 onChildrenChanged_ = CreateShared<META_NS::EventImpl<IOnChanged>>();
27
28 if (container_ = CreateContainer(); container_) {
29 if (auto required = interface_cast<IRequiredInterfaces>(container_)) {
30 // Require all children to implement IAnimation
31 required->SetRequiredInterfaces({ IAnimation::UID, IStartableAnimation::UID });
32 }
33 if (auto proxy = interface_cast<IContainerProxyParent>(container_)) {
34 // The container should use our parent animation as the parent object for children (and not itself)
35 proxy->SetProxyParent(interface_pointer_cast<IContainer>(GetOwner()));
36 }
37 containerChanged_.Subscribe(container_->OnContainerChanged(), MakeCallback<IOnChildChanged>([&](const auto& i) {
38 if (i.type == ContainerChangeType::ADDED) {
39 ChildAdded(i);
40 } else if (i.type == ContainerChangeType::REMOVED) {
41 ChildRemoved(i);
42 } else if (i.type == ContainerChangeType::MOVED) {
43 ChildMoved(i);
44 }
45 }));
46 childrenChanged_ = MakeCallback<IOnChanged>(this, &StaggeredAnimationState::ChildrenChanged);
47 return true;
48 }
49 return false;
50 }
51
Uninitialize()52 void StaggeredAnimationState::Uninitialize()
53 {
54 containerChanged_.Unsubscribe();
55 for (auto& child : children_) {
56 if (child.animation_) {
57 child.durationChanged_.Unsubscribe();
58 child.validChanged_.Unsubscribe();
59 if (auto controller = child.controller_.lock()) {
60 // If the animation was associated with a controller before it was
61 // added to this container, add it back to that controller
62 controller->AddAnimation(child.animation_);
63 }
64 }
65 }
66 Super::Uninitialize();
67 }
68
UpdateTotalDuration()69 void StaggeredAnimationState::UpdateTotalDuration()
70 {
71 baseDuration_ = TimeSpan::Zero(); // Reset base duration
72 Super::UpdateTotalDuration();
73 }
74
ChildrenChanged()75 void StaggeredAnimationState::ChildrenChanged()
76 {
77 UpdateTotalDuration();
78 Invoke<IOnChanged>(onChildrenChanged_);
79 }
80
ChildAdded(const ChildChangedInfo & info)81 void StaggeredAnimationState::ChildAdded(const ChildChangedInfo& info)
82 {
83 // Take a strong reference to the animation
84 IAnimation::Ptr animation = interface_pointer_cast<IAnimation>(info.object);
85 if (!animation) {
86 return;
87 }
88
89 // Remove the animation from it's controller as the staggered animation is handling it
90 auto controller = GetValue(animation->Controller()).lock();
91 SetValue(animation->Controller(), nullptr);
92
93 size_t inContainerCount = 0;
94 for (auto& child : GetChildren()) {
95 if (child.animation_ == animation) {
96 inContainerCount++;
97 }
98 }
99
100 AnimationSegment segment { animation, controller };
101 if (inContainerCount == 1) {
102 segment.durationChanged_.Subscribe(animation->TotalDuration(), childrenChanged_);
103 segment.validChanged_.Subscribe(animation->Valid(), childrenChanged_);
104 }
105 children_.push_back(std::move(segment));
106 ChildrenChanged();
107 }
108
ChildRemoved(const ChildChangedInfo & info)109 void StaggeredAnimationState::ChildRemoved(const ChildChangedInfo& info)
110 {
111 IAnimation::Ptr animation = interface_pointer_cast<IAnimation>(info.object);
112 for (auto it = children_.begin(); it != children_.end(); it++) {
113 if (it->animation_ == animation) {
114 RemoveChild(it);
115 ChildrenChanged();
116 break;
117 }
118 }
119 }
120
RemoveChild(typename SegmentVector::iterator & item)121 void StaggeredAnimationState::RemoveChild(typename SegmentVector::iterator& item)
122 {
123 auto& segment = *item;
124 segment.durationChanged_.Unsubscribe();
125 segment.validChanged_.Unsubscribe();
126
127 // If the animation was associated with a controller before it was
128 // added to this container, return it to that controller
129 SetValue(item->animation_->Controller(), segment.controller_.lock());
130
131 children_.erase(item);
132
133 // Empty staggered animation, remove it from it's controller
134 if (children_.empty()) {
135 if (auto controller = controller_.lock()) {
136 if (auto animation = GetOwner()) {
137 controller->RemoveAnimation(animation);
138 }
139 }
140 }
141 }
142
ChildMoved(const ChildChangedInfo & info)143 void StaggeredAnimationState::ChildMoved(const ChildChangedInfo& info)
144 {
145 auto fromIndex = info.from;
146 auto toIndex = info.to;
147 const auto size = children_.size();
148 fromIndex = BASE_NS::Math::min(fromIndex, size - 1);
149 toIndex = BASE_NS::Math::min(toIndex, size - 1);
150 if (fromIndex == toIndex) {
151 return;
152 }
153 auto& child = children_[fromIndex];
154 if (fromIndex > toIndex) {
155 const auto first = children_.rbegin() + static_cast<SegmentVector::difference_type>(size - fromIndex - 1);
156 const auto last = children_.rbegin() + static_cast<SegmentVector::difference_type>(size - toIndex);
157 std::rotate(first, first + 1, last);
158 } else {
159 const auto first = children_.begin() + static_cast<SegmentVector::difference_type>(fromIndex);
160 const auto last = children_.begin() + static_cast<SegmentVector::difference_type>(toIndex + 1);
161 std::rotate(first, first + 1, last);
162 }
163 ChildrenChanged();
164 }
165
IsValid() const166 bool StaggeredAnimationState::IsValid() const
167 {
168 // If any of the children of this container are valid, then
169 // the container is valid
170 for (const auto& child : children_) {
171 auto& animation = child.animation_;
172 if (animation && GetValue(animation->Valid())) {
173 return true;
174 }
175 }
176 return false;
177 }
178
MapTo01Range(float value,float inputStart,float inputEnd,bool reverse)179 constexpr float MapTo01Range(float value, float inputStart, float inputEnd, bool reverse) noexcept
180 {
181 if (reverse) {
182 const auto offset = 1.f - inputEnd;
183 inputStart += offset;
184 inputEnd += offset;
185 }
186 return (1 / (inputEnd - inputStart)) * (value - inputStart);
187 }
188
TransformChild(const StaggeredAnimationState::AnimationSegment & segment,float parentProgress,IAnimationInternal::AnimationTargetState parentState,bool reverse)189 constexpr IAnimationInternal::MoveParams TransformChild(const StaggeredAnimationState::AnimationSegment& segment,
190 float parentProgress, IAnimationInternal::AnimationTargetState parentState, bool reverse) noexcept
191 {
192 using AnimationTargetState = IAnimationInternal::AnimationTargetState;
193 IAnimationInternal::MoveParams params;
194 params.step.progress = MapTo01Range(parentProgress, segment.startProgress_, segment.endProgress_, reverse);
195
196 if (parentState == AnimationTargetState::RUNNING) {
197 params.state = AnimationTargetState::RUNNING;
198 if (params.step.progress < 0.f) {
199 params.state = AnimationTargetState::STOPPED;
200 } else if (params.step.progress >= 1.f) {
201 params.state = AnimationTargetState::FINISHED;
202 }
203 } else {
204 params.state = parentState;
205 }
206
207 if (reverse) {
208 params.step.progress = 1.f - params.step.progress;
209 }
210 params.step.reverse = reverse;
211 return params;
212 }
213
214 // ParallelAnimationState
215
CreateContainer() const216 IContainer::Ptr ParallelAnimationState::CreateContainer() const
217 {
218 return META_NS::GetObjectRegistry().Create<IContainer>(ClassId::ObjectContainer);
219 }
220
GetAnimationBaseDuration() const221 TimeSpan ParallelAnimationState::GetAnimationBaseDuration() const
222 {
223 if (baseDuration_ != TimeSpan::Zero()) {
224 return baseDuration_;
225 }
226 // Duration of a ParallelAnimation is the max of the total duration of children
227 TimeSpan totalDuration = TimeSpan::Zero();
228 for (const auto& segment : GetChildren()) {
229 const auto& animation = segment.animation_;
230 if (animation) {
231 const TimeSpan childDuration = GetValue(animation->TotalDuration(), TimeSpan::Zero());
232 totalDuration = BASE_NS::Math::max(totalDuration, childDuration);
233 }
234 }
235 return totalDuration;
236 }
237
ChildrenChanged()238 void ParallelAnimationState::ChildrenChanged()
239 {
240 Super::ChildrenChanged();
241 auto containerDuration = GetAnimationBaseDuration();
242 for (auto&& segment : GetChildren()) {
243 segment.startProgress_ = 0;
244 segment.endProgress_ = 1.f;
245 if (containerDuration.IsFinite()) {
246 const auto duration = segment.animation_ ? GetValue(segment.animation_->TotalDuration()) : TimeSpan::Zero();
247 segment.endProgress_ =
248 static_cast<float>(duration.ToMilliseconds()) / static_cast<float>(containerDuration.ToMilliseconds());
249 }
250 }
251 }
252
Evaluate()253 AnyReturnValue ParallelAnimationState::Evaluate()
254 {
255 AnyReturnValue status = AnyReturn::NOTHING_TO_DO;
256 const float containerProgress = GetValue(GetOwner()->Progress());
257 const auto step = ApplyStepModifiers(containerProgress);
258 for (const auto& segment : GetChildren()) {
259 if (const auto internal = interface_cast<IAnimationInternal>(segment.animation_)) {
260 if (internal->Move(TransformChild(segment, containerProgress, GetAnimationTargetState(), step.reverse))) {
261 status = AnyReturn::SUCCESS;
262 }
263 }
264 }
265 return status;
266 }
267
268 // SequentialAnimationState
269
CreateContainer() const270 IContainer::Ptr SequentialAnimationState::CreateContainer() const
271 {
272 return META_NS::GetObjectRegistry().Create<IContainer>(ClassId::ObjectFlatContainer);
273 }
274
GetAnimationBaseDuration() const275 TimeSpan SequentialAnimationState::GetAnimationBaseDuration() const
276 {
277 if (baseDuration_ != TimeSpan::Zero()) {
278 return baseDuration_;
279 }
280 // Duration of a SequentialAnimation is the sum of the total duration of children
281 TimeSpan totalDuration = TimeSpan::Zero();
282 for (const auto& segment : GetChildren()) {
283 const auto& animation = segment.animation_;
284 if (animation) {
285 totalDuration += GetValue(animation->TotalDuration(), TimeSpan::Zero());
286 }
287 }
288 return totalDuration;
289 }
290
ChildrenChanged()291 void SequentialAnimationState::ChildrenChanged()
292 {
293 Super::ChildrenChanged();
294 auto containerDuration = GetAnimationBaseDuration();
295 float startProgress = 0.f;
296
297 for (auto&& segment : GetChildren()) {
298 segment.startProgress_ = startProgress;
299 segment.endProgress_ = 1.f;
300 if (containerDuration.IsFinite()) {
301 const auto duration = segment.animation_ ? GetValue(segment.animation_->TotalDuration()) : TimeSpan::Zero();
302 segment.endProgress_ = startProgress + static_cast<float>(duration.ToMilliseconds()) /
303 static_cast<float>(containerDuration.ToMilliseconds());
304 startProgress = segment.endProgress_;
305 } else {
306 CORE_LOG_E("Infinite animation in a sequential animation");
307 startProgress = 1.f;
308 }
309 }
310 }
311
GetActiveAnimation(float progress,bool reverse) const312 SequentialAnimationState::ActiveAnimation SequentialAnimationState::GetActiveAnimation(
313 float progress, bool reverse) const
314 {
315 const auto& children = GetChildren();
316
317 int64_t index = 0;
318 for (const auto& anim : GetChildren()) {
319 const auto transform = TransformChild(anim, progress, GetAnimationTargetState(), reverse);
320 if (transform.state == IAnimationInternal::AnimationTargetState::RUNNING) {
321 return { &anim, index };
322 }
323 index++;
324 }
325 return { nullptr, children.empty() ? -1 : index };
326 }
327
Evaluate()328 AnyReturnValue SequentialAnimationState::Evaluate()
329 {
330 AnyReturnValue status = AnyReturn::NOTHING_TO_DO;
331 const float containerProgress = GetValue(GetOwner()->Progress());
332 const auto step = ApplyStepModifiers(containerProgress);
333 const auto& children = GetChildren();
334
335 auto active = GetActiveAnimation(containerProgress, step.reverse);
336 if (!active) {
337 // No active animation, make sure all animations are stopped/finished
338 for (const auto& segment : children) {
339 if (auto internal = interface_cast<IAnimationInternal>(segment.animation_)) {
340 internal->Move(TransformChild(segment, containerProgress, GetAnimationTargetState(), step.reverse));
341 }
342 }
343 return children.empty() ? AnyReturn::SUCCESS : AnyReturn::NOTHING_TO_DO;
344 }
345
346 // Iterate first->last or last->first depending on direction
347 int mod = 1;
348 size_t index = 0;
349 size_t target = children.size();
350 if (step.reverse) {
351 mod = -1;
352 std::swap(index, target);
353 }
354 for (; index != target; index += mod) {
355 const auto& segment = children[index];
356 const auto params = TransformChild(children[index], containerProgress, GetAnimationTargetState(), step.reverse);
357 if (params.state == IAnimationInternal::AnimationTargetState::RUNNING ||
358 segment.animation_ != active.segment->animation_) {
359 // If one instance of the animation is in running state, don't touch the animation for any other state
360 if (const auto internal = interface_cast<IAnimationInternal>(segment.animation_)) {
361 internal->Move(params);
362 }
363 }
364 }
365
366 return status;
367 }
368
369 } // namespace Internal
370
371 META_END_NAMESPACE()
372