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