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