• 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 "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