• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/animation/card_transition_controller.h"
17 
18 #include "core/animation/curve_animation.h"
19 #include "core/components/list/list_item_element.h"
20 #include "core/components/list/render_list.h"
21 #include "core/components/page_transition/page_transition_element.h"
22 #include "core/components/transform/transform_element.h"
23 
24 namespace OHOS::Ace {
25 namespace {
26 
27 constexpr float OPACITY_CHANGE_END = 0.286f;
28 constexpr float OPACITY_CHANGE_START = 0.571f;
29 
30 } // namespace
31 
CardTransitionController(const WeakPtr<PipelineContext> & context)32 CardTransitionController::CardTransitionController(const WeakPtr<PipelineContext>& context) : context_(context)
33 {
34     controller_ = CREATE_ANIMATOR(context);
35 };
36 
RegisterTransitionListener()37 void CardTransitionController::RegisterTransitionListener()
38 {
39     auto pipelineContext = context_.Upgrade();
40     if (!pipelineContext) {
41         LOGE("Register Transition listener to stage failed. pipeline context is null.");
42         return;
43     }
44     auto weak = AceType::WeakClaim(this);
45     pipelineContext->AddPageTransitionListener(
46         [weak](const TransitionEvent& event, const WeakPtr<PageElement>& in, const WeakPtr<PageElement>& out) {
47             if ((event != TransitionEvent::POP_START) && (event != TransitionEvent::PUSH_START)) {
48                 return;
49             }
50             auto controller = weak.Upgrade();
51             if (!controller) {
52                 LOGE("handle event: %{public}d failed. controller is null.", event);
53                 return;
54             }
55             if (event == TransitionEvent::PUSH_START) {
56                 controller->pageDest_ = in;
57                 controller->pageSrc_ = out;
58             } else {
59                 controller->pageDest_ = out;
60                 controller->pageSrc_ = in;
61             }
62             controller->event_ = event;
63             controller->PrepareTransition();
64         });
65 }
66 
PrepareTransition()67 void CardTransitionController::PrepareTransition()
68 {
69     // finish previous transition
70     controller_->Finish();
71     // clear controller
72     controller_->ClearAllListeners();
73     controller_->ClearInterpolators();
74     controller_->ClearPrepareListeners();
75     auto pipelineContext = context_.Upgrade();
76     if (!pipelineContext) {
77         LOGE("prepare card transition failed. pipeline is null.");
78         return;
79     }
80     auto pageDest = (event_ == TransitionEvent::PUSH_START) ? pageDest_.Upgrade() : pageSrc_.Upgrade();
81     if (!pageDest || pageDest->GetCardComposeId().empty()) {
82         return;
83     }
84     auto composeId = pageDest->GetCardComposeId();
85     auto cardComposeElement = pipelineContext->GetComposedElementById(composeId);
86     if (!cardComposeElement) {
87         LOGE("prepare card transition failed. cardComposeElement is null.");
88         return;
89     }
90     auto cardRender = cardComposeElement->GetRenderNode();
91     if (!cardRender) {
92         LOGE("prepare card transition failed. cardRender is null.");
93         return;
94     }
95     auto rrect = GetCardRect(composeId);
96     CreateCardAnimation(cardComposeElement, cardRender->GetGlobalOffset(), rrect);
97     CreateCardListAnimation(cardRender);
98     CreateExternalAnimation(cardRender->GetLayoutSize().Height(), cardRender->GetGlobalOffset().GetY());
99 }
100 
CreateCardAnimation(const RefPtr<Element> & cardComposeElement,const Offset & globalOffset,const RRect & rrect)101 void CardTransitionController::CreateCardAnimation(const RefPtr<Element>& cardComposeElement,
102     const Offset& globalOffset, const RRect& rrect)
103 {
104     auto listItemElement = cardComposeElement->GetFirstChild();
105     if (!listItemElement) {
106         LOGE("create card animation failed. listItemElement is null.");
107         return;
108     }
109     auto displayElement = AceType::DynamicCast<DisplayElement>(listItemElement->GetFirstChild());
110     if (!displayElement) {
111         LOGE("create card animation failed. displayElement is null.");
112         return;
113     }
114     auto displayRender = displayElement->GetRenderNode();
115     if (!displayRender) {
116         LOGE("create card animation failed. displayRender is null.");
117         return;
118     }
119     auto transformElement = GetCardTransformElement(cardComposeElement);
120     if (!transformElement) {
121         LOGE("create card animation failed. transformElement is null.");
122         return;
123     }
124     auto transformRender = AceType::DynamicCast<RenderTransform>(transformElement->GetRenderNode());
125     if (!transformRender) {
126         LOGE("create card animation failed. transformRender is null.");
127         return;
128     }
129     auto weakTransform = AceType::WeakClaim(AceType::RawPtr(transformRender));
130     CreateCardScaleAnimation(weakTransform, rrect);
131     CreateCardTranslateAnimation(weakTransform, globalOffset, rrect.GetRect().GetOffset() - globalOffset);
132     CreateCardOpacityAnimation(displayRender);
133     AddPrepareListener(weakTransform);
134     AddStopListener(weakTransform);
135 }
136 
CreateCardTranslateAnimation(const WeakPtr<RenderTransform> & weakTransform,const Offset & globalOffset,const Offset & marginOffset)137 void CardTransitionController::CreateCardTranslateAnimation(const WeakPtr<RenderTransform>& weakTransform,
138     const Offset& globalOffset, const Offset& marginOffset)
139 {
140     auto pipelineContext = context_.Upgrade();
141     if (!pipelineContext) {
142         LOGE("create card translate animation failed. pipeline is null.");
143         return;
144     }
145     Offset startOffset = (event_ == TransitionEvent::PUSH_START) ? Offset() : Offset() - (globalOffset + marginOffset);
146     Offset endOffset = (event_ == TransitionEvent::PUSH_START) ? Offset() - (globalOffset + marginOffset) : Offset();
147     auto offsetAnimation = AceType::MakeRefPtr<CurveAnimation<Offset>>(startOffset, endOffset, Curves::FRICTION);
148     offsetAnimation->AddListener([weakTransform](const Offset& offset) {
149         auto transform = weakTransform.Upgrade();
150         if (transform) {
151             transform->Translate(Dimension(offset.GetX()), Dimension(offset.GetY()));
152         }
153     });
154     controller_->AddInterpolator(offsetAnimation);
155 }
156 
CreateCardScaleAnimation(const WeakPtr<RenderTransform> & weakTransform,const RRect & rrect)157 void CardTransitionController::CreateCardScaleAnimation(const WeakPtr<RenderTransform>& weakTransform,
158     const RRect& rrect)
159 {
160     auto pipelineContext = context_.Upgrade();
161     if (!pipelineContext) {
162         LOGE("create card scale animation failed. pipeline is null.");
163         return;
164     }
165     auto transform = weakTransform.Upgrade();
166     if (!transform) {
167         LOGE("create card scale animation failed. transform is null.");
168         return;
169     }
170     auto width = rrect.Width();
171     if (NearEqual(width, 0.0)) {
172         LOGE("create card animation failed. width is zero.");
173         return;
174     }
175     transform->SetDisableClickEffect(true);
176     transform->ResetTransform();
177     transform->SetTransformOrigin(Dimension(), Dimension());
178     transform->MarkNeedUpdateOrigin();
179 
180     double startScale = (event_ == TransitionEvent::PUSH_START) ? 1.0 : pipelineContext->GetRootWidth() / width;
181     double endScale = (event_ == TransitionEvent::PUSH_START) ? pipelineContext->GetRootWidth() / width : 1.0;
182     auto scaleAnimation = AceType::MakeRefPtr<CurveAnimation<double>>(startScale, endScale, Curves::FRICTION);
183     scaleAnimation->AddListener([weakTransform](double value) {
184         auto transform = weakTransform.Upgrade();
185         if (transform) {
186             transform->Scale(value);
187         }
188     });
189     controller_->AddInterpolator(scaleAnimation);
190 }
191 
CreateCardOpacityAnimation(RefPtr<RenderNode> & displayRender)192 void CardTransitionController::CreateCardOpacityAnimation(RefPtr<RenderNode>& displayRender)
193 {
194     auto opacityAnimation = AceType::MakeRefPtr<KeyframeAnimation<uint8_t>>();
195     if (event_ == TransitionEvent::PUSH_START) {
196         auto opacityKeyframe1 = AceType::MakeRefPtr<Keyframe<uint8_t>>(0.0f, 255);
197         auto opacityKeyframe2 = AceType::MakeRefPtr<Keyframe<uint8_t>>(OPACITY_CHANGE_END, 0);
198         opacityKeyframe2->SetCurve(Curves::SHARP);
199         auto opacityKeyframe3 = AceType::MakeRefPtr<Keyframe<uint8_t>>(1.0f, 0);
200         opacityAnimation->AddKeyframe(opacityKeyframe1);
201         opacityAnimation->AddKeyframe(opacityKeyframe2);
202         opacityAnimation->AddKeyframe(opacityKeyframe3);
203     } else {
204         auto opacityKeyframe1 = AceType::MakeRefPtr<Keyframe<uint8_t>>(0.0f, 0);
205         auto opacityKeyframe2 = AceType::MakeRefPtr<Keyframe<uint8_t>>(OPACITY_CHANGE_START, 0);
206         auto opacityKeyframe3 = AceType::MakeRefPtr<Keyframe<uint8_t>>(1.0f, 255);
207         opacityKeyframe3->SetCurve(Curves::SHARP);
208         opacityAnimation->AddKeyframe(opacityKeyframe1);
209         opacityAnimation->AddKeyframe(opacityKeyframe2);
210         opacityAnimation->AddKeyframe(opacityKeyframe3);
211     }
212     opacityAnimation->AddListener([displayWeak = AceType::WeakClaim(AceType::RawPtr(displayRender))](uint8_t value) {
213         auto display = displayWeak.Upgrade();
214         if (display) {
215             display->UpdateOpacity(value);
216         }
217     });
218     controller_->AddInterpolator(opacityAnimation);
219 }
220 
CreateCardListAnimation(const RefPtr<RenderNode> & renderNode)221 void CardTransitionController::CreateCardListAnimation(const RefPtr<RenderNode>& renderNode)
222 {
223     auto renderListItem = AceType::DynamicCast<RenderListItem>(renderNode);
224     if (!renderListItem) {
225         return;
226     }
227     auto height = renderListItem->GetLayoutSize().Height();
228     auto start = (event_ == TransitionEvent::PUSH_START) ? 0.0 : height;
229     auto end = (event_ == TransitionEvent::PUSH_START) ? height : 0.0;
230     auto heightAnimation = AceType::MakeRefPtr<CurveAnimation<double>>(start, end, Curves::FRICTION);
231     auto listItemWeak = AceType::WeakClaim(AceType::RawPtr(renderListItem));
232     heightAnimation->AddListener([listItemWeak](double value) {
233         auto renderListItem = listItemWeak.Upgrade();
234         if (renderListItem) {
235             renderListItem->RunCardTransitionAnimation(value);
236         }
237     });
238     controller_->AddInterpolator(heightAnimation);
239     controller_->AddStopListener([listItemWeak] {
240         auto renderListItem = listItemWeak.Upgrade();
241         if (renderListItem) {
242             renderListItem->StopCardTransitionAnimation();
243         }
244     });
245 }
246 
CreateExternalAnimation(double height,double cardOffsetY)247 void CardTransitionController::CreateExternalAnimation(double height, double cardOffsetY)
248 {
249     auto pageSrc = (event_ == TransitionEvent::PUSH_START) ? pageSrc_.Upgrade() : pageDest_.Upgrade();
250     if (!pageSrc) {
251         return;
252     }
253     const auto& transformMap = pageSrc->GetCardTransitionMap();
254     for (auto& item : transformMap) {
255         auto& transformWeak = item.second;
256         auto transformElement = transformWeak.Upgrade();
257         if (!transformElement) {
258             continue;
259         }
260         auto transformRender = AceType::DynamicCast<RenderTransform>(transformElement->GetRenderNode());
261         if (!transformRender) {
262             continue;
263         }
264         auto transformOffsetY = transformRender->GetGlobalOffset().GetY();
265         if (NearEqual(transformOffsetY, cardOffsetY)) {
266             continue;
267         }
268         auto shiftHeight = height;
269         if (transformOffsetY < cardOffsetY) {
270             shiftHeight = -height;
271         }
272         auto start = (event_ == TransitionEvent::PUSH_START) ? 0.0 : shiftHeight;
273         auto end = (event_ == TransitionEvent::PUSH_START) ? shiftHeight : 0.0;
274         auto positionAnimation = AceType::MakeRefPtr<CurveAnimation<double>>(start, end, Curves::FRICTION);
275         auto weakTransform = AceType::WeakClaim(AceType::RawPtr(transformRender));
276         positionAnimation->AddListener([weakTransform](double value) {
277             auto transform = weakTransform.Upgrade();
278             if (transform) {
279                 transform->ResetTransform();
280                 transform->Translate(Dimension(), Dimension(value));
281             }
282         });
283         controller_->AddInterpolator(positionAnimation);
284         controller_->AddStopListener([weakTransform, event = event_] {
285             auto transform = weakTransform.Upgrade();
286             if (transform && event == TransitionEvent::PUSH_START) {
287                 transform->ResetTransform();
288             }
289         });
290     }
291 }
292 
AddPrepareListener(const WeakPtr<RenderTransform> & weakTransform)293 void CardTransitionController::AddPrepareListener(const WeakPtr<RenderTransform>& weakTransform)
294 {
295     controller_->AddPrepareListener([weakTransform, weakContext = context_, weak = AceType::WeakClaim(this)]() {
296         auto context = weakContext.Upgrade();
297         auto cardTransition = weak.Upgrade();
298         auto transform = weakTransform.Upgrade();
299         if (context && cardTransition && transform) {
300             auto currentTimestamp = context->GetTimeFromExternalTimer();
301             if (cardTransition->currentTimestamp_ != currentTimestamp || cardTransition->currentTimestamp_ == 0) {
302                 transform->ResetTransform();
303                 cardTransition->currentTimestamp_ = currentTimestamp;
304             }
305         }
306     });
307 }
308 
AddStopListener(const WeakPtr<RenderTransform> & weakTransform)309 void CardTransitionController::AddStopListener(const WeakPtr<RenderTransform>& weakTransform)
310 {
311     controller_->AddStopListener([weakTransform] {
312         auto transform = weakTransform.Upgrade();
313         if (transform) {
314             transform->ResetTransform();
315             transform->SetTransformOrigin(Dimension(transform->GetLayoutSize().Width() * 0.5),
316                 Dimension(transform->GetLayoutSize().Height() * 0.5));
317             transform->MarkNeedUpdateOrigin();
318             transform->SetDisableClickEffect(false);
319         }
320     });
321 }
322 
GetCardTransformElement(const RefPtr<Element> & element)323 RefPtr<Element> CardTransitionController::GetCardTransformElement(const RefPtr<Element>& element)
324 {
325     auto childElement = element->GetFirstChild();
326     while (childElement) {
327         auto composedElement =  AceType::DynamicCast<ComposedElement>(childElement);
328         if (composedElement) {
329             return nullptr;
330         }
331         auto transformElement =  AceType::DynamicCast<TransformElement>(childElement);
332         if (transformElement) {
333             return transformElement;
334         }
335         childElement = childElement->GetFirstChild();
336     }
337     return nullptr;
338 }
339 
GetCardRect(const ComposeId & composeId) const340 RRect CardTransitionController::GetCardRect(const ComposeId& composeId) const
341 {
342     auto pipelineContext = context_.Upgrade();
343     if (!pipelineContext) {
344         LOGE("get card rect failed. pipeline is null.");
345         return RRect();
346     }
347     auto cardComposeElement = pipelineContext->GetComposedElementById(composeId);
348     if (!cardComposeElement) {
349         return RRect();
350     }
351     auto renderListItem = AceType::DynamicCast<RenderListItem>(cardComposeElement->GetRenderNode());
352     if (!renderListItem) {
353         return RRect();
354     }
355     return renderListItem->GetRRect();
356 }
357 
358 } // namespace OHOS::Ace
359