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