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