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/shared_transition_effect.h"
17
18 #include "core/animation/animation_pub.h"
19 #include "core/animation/animator.h"
20 #include "core/animation/curve_animation.h"
21 #include "core/animation/keyframe.h"
22 #include "core/components/common/properties/motion_path_evaluator.h"
23 #include "core/components/common/properties/page_transition_option.h"
24 #include "core/components/overlay/overlay_element.h"
25 #include "core/components/positioned/positioned_component.h"
26 #include "core/components/positioned/render_positioned.h"
27 #include "core/components/shared_transition/shared_transition_element.h"
28 #include "core/components/tween/tween_component.h"
29
30 namespace OHOS::Ace {
31
SharedTransitionEffect(const ShareId & shareId,SharedTransitionEffectType type)32 SharedTransitionEffect::SharedTransitionEffect(const ShareId& shareId, SharedTransitionEffectType type)
33 : shareId_(shareId), type_(type)
34 {
35 controller_ = AceType::MakeRefPtr<Animator>();
36 }
37
CheckIn(TransitionEvent event,WeakPtr<SharedTransitionElement> & sharedWeak,Offset & ticket)38 bool SharedTransitionEffect::CheckIn(
39 TransitionEvent event, WeakPtr<SharedTransitionElement>& sharedWeak, Offset& ticket)
40 {
41 if (!controller_) {
42 LOGE("Check In failed. controller is null. share id: %{public}s", shareId_.c_str());
43 return false;
44 }
45 auto shared = sharedWeak.Upgrade();
46 if (!shared) {
47 LOGE("Check In failed. passenger element is null. share id: %{public}s", shareId_.c_str());
48 return false;
49 }
50 // Check-in
51 if (!shared->AboardShuttle(ticket)) {
52 LOGE("Check In failed. aboard shuttle failed. share id: %{public}s.", shareId_.c_str());
53 return false;
54 }
55 // Arrange Return Shuttle
56 controller_->AddStopListener([sharedWeak, shareId = shareId_]() {
57 auto shared = sharedWeak.Upgrade();
58 if (!shared) {
59 // In Pop Scene, target page may be removed, and will reach this branch.
60 LOGD("Passenger missing return shuttle. shared element is null. shareId: %{public}s", shareId.c_str());
61 return;
62 }
63 shared->GetOffShuttle();
64 });
65 controller_->AddStopListener([destWeak = dest_, srcWeak = src_]() {
66 auto dest = destWeak.Upgrade();
67 if (dest) {
68 dest->SetSizeModified(nullptr);
69 }
70 auto src = srcWeak.Upgrade();
71 if (src) {
72 src->SetSizeModified(nullptr);
73 }
74 });
75 return true;
76 }
77
TakeOff(TransitionEvent event,RefPtr<OverlayElement> & overlay,WeakPtr<SharedTransitionElement> & sharedWeak,const Offset & ticket,TweenOption & option)78 bool SharedTransitionEffect::TakeOff(TransitionEvent event, RefPtr<OverlayElement>& overlay,
79 WeakPtr<SharedTransitionElement>& sharedWeak, const Offset& ticket, TweenOption& option)
80 {
81 if (!controller_) {
82 LOGE("TakeOff failed. controller is null. share id: %{public}s", shareId_.c_str());
83 return false;
84 }
85 if (!overlay) {
86 LOGE("TakeOff failed. overlay is null. event: %{public}d, share id: %{public}s", event, shareId_.c_str());
87 return false;
88 }
89 auto shared = sharedWeak.Upgrade();
90 if (!shared) {
91 LOGE("TakeOff failed. shared is null. event: %{public}d, share id: %{public}s", event, shareId_.c_str());
92 return false;
93 }
94 auto passengerComponent = shared->GetPassengerComponent();
95 auto passengerElement = shared->GetPassengerElement();
96 if (!passengerComponent || !passengerElement) {
97 LOGE("TakeOff failed. passenger not found. event: %{public}d, share id: %{public}s.", event, shareId_.c_str());
98 return false;
99 }
100 auto tweenSeat = AceType::MakeRefPtr<TweenComponent>("TweenSeat");
101 // Find Seat.
102 auto seat = AceType::MakeRefPtr<PositionedComponent>(tweenSeat);
103 Component::MergeRSNode(seat);
104 LOGD("TakeOff. event: %{public}d, share id: %{public}s. x: %{public}lf, y: %{public}lf", event, shareId_.c_str(),
105 ticket.GetX(), ticket.GetY());
106 seat->SetLeft(Dimension(ticket.GetX(), DimensionUnit::PX));
107 seat->SetTop(Dimension(ticket.GetY(), DimensionUnit::PX));
108 // set zIndex
109 auto zIndex = shared->GetZIndex();
110 if (zIndex != 0) {
111 seat->SetZIndex(zIndex);
112 }
113 // Take Off,
114 overlay->PushInstant(seat);
115 auto seatElement = AceType::DynamicCast<PositionedElement>(overlay->GetLastChild());
116 if (!seatElement) {
117 LOGE("TakeOff failed. seat not found. event: %{public}d, share id: %{public}s.", event, shareId_.c_str());
118 return false;
119 }
120 // make overlay un-focusable.
121 seatElement->SetFocusable(false);
122 auto seatRender = seatElement->GetRenderNode();
123 if (!seatRender) {
124 LOGE("TakeOff failed, render is nullptr");
125 return false;
126 }
127 seatRender->SetDisableTouchEvent(true);
128 return TakeOffTween(seatElement->GetFirstChild(), passengerComponent, passengerElement, option);
129 }
130
TakeOffTween(const RefPtr<Element> & tweenElement,const RefPtr<Component> & passengerComponent,const RefPtr<Element> & passengerElement,TweenOption & option)131 bool SharedTransitionEffect::TakeOffTween(const RefPtr<Element>& tweenElement,
132 const RefPtr<Component>& passengerComponent, const RefPtr<Element>& passengerElement, TweenOption& option)
133 {
134 auto tweenSeatElement = AceType::DynamicCast<TweenElement>(tweenElement);
135 if (!tweenSeatElement) {
136 LOGE("TakeOff failed. tween not found. share id: %{public}s.", shareId_.c_str());
137 return false;
138 }
139 auto contentParent = tweenSeatElement->GetContentParent();
140 if (!contentParent) {
141 LOGE("TakeOff failed. content parent not found. share id: %{public}s.", shareId_.c_str());
142 return false;
143 }
144 passengerElement->SetNewComponent(passengerComponent);
145 passengerElement->Mount(contentParent);
146 auto passengerRender = passengerElement->GetRenderNode();
147 auto parentRender = contentParent->GetRenderNode();
148 if (passengerRender && parentRender) {
149 // Follow parent's hidden status.
150 passengerRender->SetHidden(parentRender->GetHidden());
151 }
152 tweenSeatElement->SetController(controller_);
153 tweenSeatElement->SetOption(option);
154 tweenSeatElement->ApplyKeyframes();
155 tweenSeatElement->ApplyOptions();
156 tweenSeatElement_ = tweenSeatElement;
157 return true;
158 }
159
GetSharedTransitionEffect(SharedTransitionEffectType effect,const ShareId & shareId)160 RefPtr<SharedTransitionEffect> SharedTransitionEffect::GetSharedTransitionEffect(
161 SharedTransitionEffectType effect, const ShareId& shareId)
162 {
163 LOGD("Get shared transition effect. effect: %{public}d, share id: %{public}s", effect, shareId.c_str());
164 switch (effect) {
165 case SharedTransitionEffectType::SHARED_EFFECT_EXCHANGE: {
166 return AceType::MakeRefPtr<SharedTransitionExchange>(shareId);
167 }
168 case SharedTransitionEffectType::SHARED_EFFECT_STATIC: {
169 return AceType::MakeRefPtr<SharedTransitionStatic>(shareId);
170 }
171 default: {
172 LOGE("Unknown effect. effect: %{public}d, share id: %{public}s", effect, shareId.c_str());
173 return nullptr;
174 }
175 }
176 }
177
ApplyAnimation(RefPtr<OverlayElement> & overlay,RefPtr<Animator> & controller,TweenOption & option,TransitionEvent event)178 bool SharedTransitionEffect::ApplyAnimation(
179 RefPtr<OverlayElement>& overlay, RefPtr<Animator>& controller, TweenOption& option, TransitionEvent event)
180 {
181 controller_->ClearAllListeners();
182 controller_->ClearInterpolators();
183 return true;
184 }
185
Allow(TransitionEvent event)186 bool SharedTransitionExchange::Allow(TransitionEvent event)
187 {
188 auto dest = dest_.Upgrade();
189 auto src = src_.Upgrade();
190 if (!dest || !src) {
191 LOGD("Check allow exchange failed. dest or src is null. event: %{public}d, share id: %{public}s", event,
192 shareId_.c_str());
193 return false;
194 }
195 bool allow = false;
196 if (event == TransitionEvent::PUSH_START) {
197 // In Push Scene, dest means Enter and Source means Exit
198 allow = dest->IsEnablePushEnter() && src->IsEnablePushExit();
199 } else if (event == TransitionEvent::POP_START) {
200 // In Pop Scene, dest means Enter and Source means Exit
201 allow = dest->IsEnablePopEnter() && src->IsEnablePopExit();
202 }
203 LOGD("Exchange Allow status: %{public}d, event: %{public}d, share id: %{public}s", allow, event, shareId_.c_str());
204 return allow;
205 }
206
AddLazyLoadCallback(TransitionEvent event)207 void SharedTransitionExchange::AddLazyLoadCallback(TransitionEvent event)
208 {
209 auto src = src_.Upgrade();
210 auto dest = dest_.Upgrade();
211 if (!dest || !src) {
212 LOGE("Add Lazy callback failed. Dest or src is null. event: %{public}d, share id: %{public}s", event,
213 shareId_.c_str());
214 return;
215 }
216 // Lazy load child size. make width and height animation later.
217 auto effectWeak = AceType::WeakClaim(this);
218 dest->SetSizeModified([effectWeak, event, shareId = shareId_]() {
219 LOGD("Lazy load size callback. event: %{public}d, share id: %{public}s", event, shareId.c_str());
220 auto effect = effectWeak.Upgrade();
221 if (!effect) {
222 LOGE("Create Lazy load animation failed. effect is null.");
223 return;
224 }
225 auto tweenSeatElement = effect->tweenSeatElement_.Upgrade();
226 if (!tweenSeatElement) {
227 LOGE("Create Lazy load animation failed. tween Seat Element is null.");
228 return;
229 }
230 TweenOption option = tweenSeatElement->GetOption();
231 option.ClearListeners();
232 if (!effect->CreateAnimation(option, event, true)) {
233 LOGE("Create animation failed. event: %{public}d, share id: %{public}s", event, shareId.c_str());
234 return;
235 }
236
237 tweenSeatElement->SetOption(option);
238 effect->controller_->ClearInterpolators();
239 tweenSeatElement->ApplyKeyframes();
240 });
241 }
242
CreateTranslateAnimation(TweenOption & option,TransitionEvent event,bool calledByLazyLoad)243 bool SharedTransitionExchange::CreateTranslateAnimation(
244 TweenOption& option, TransitionEvent event, bool calledByLazyLoad)
245 {
246 auto src = src_.Upgrade();
247 auto dest = dest_.Upgrade();
248 if (!dest || !src) {
249 LOGE("Create exchange animation failed. dest or src is null. event: %{public}d, share id: %{public}s", event,
250 shareId_.c_str());
251 return false;
252 }
253 auto& translateMap = option.GetTranslateAnimations();
254 auto translateIter = translateMap.find(AnimationType::TRANSLATE);
255 if ((calledByLazyLoad && autoTranslate_) || (translateIter == translateMap.end())) {
256 // if no custom translate animation, add exchange translate animation.
257 auto destOffset = dest->GetGlobalOffset();
258 auto srcOffset = src->GetGlobalOffset();
259 LOGD("Get Offset for event: %{public}d, share id: %{public}s. dest: x: %{public}lf, y: %{public}lf; "
260 "src: x: %{public}lf, y: %{public}lf",
261 event, shareId_.c_str(), destOffset.GetX(), destOffset.GetY(), srcOffset.GetX(), srcOffset.GetY());
262 if (destOffset != srcOffset) {
263 auto translateAnimation = AceType::MakeRefPtr<CurveAnimation<DimensionOffset>>(
264 Offset(0, 0), destOffset - srcOffset, Curves::FRICTION);
265 const auto& motionPathOption = option.GetMotionPathOption();
266 if (motionPathOption.IsValid()) {
267 auto motionPathEvaluator =
268 AceType::MakeRefPtr<MotionPathEvaluator>(motionPathOption, Offset(0, 0), destOffset - srcOffset);
269 translateAnimation->SetEvaluator(motionPathEvaluator->CreateDimensionOffsetEvaluator());
270 if (motionPathOption.GetRotate()) {
271 auto rotateAnimation = AceType::MakeRefPtr<CurveAnimation<float>>(0.0f, 1.0f, option.GetCurve());
272 rotateAnimation->SetEvaluator(motionPathEvaluator->CreateRotateEvaluator());
273 option.SetTransformFloatAnimation(AnimationType::ROTATE_Z, rotateAnimation);
274 }
275 }
276 option.SetTranslateAnimations(AnimationType::TRANSLATE, translateAnimation);
277 autoTranslate_ = true;
278 LOGD("Create shared exchange animation for translate. event: %{public}d, share id: %{public}s", event,
279 shareId_.c_str());
280 }
281 }
282 return true;
283 }
284
CreateSizeAnimation(TweenOption & option,TransitionEvent event,bool isLazy)285 bool SharedTransitionExchange::CreateSizeAnimation(TweenOption& option, TransitionEvent event, bool isLazy)
286 {
287 auto src = src_.Upgrade();
288 auto dest = dest_.Upgrade();
289 if (!dest || !src) {
290 LOGE("Create exchange animation failed. dest or src is null. event: %{public}d, share id: %{public}s", event,
291 shareId_.c_str());
292 return false;
293 }
294 auto destSize = dest->GetSuitSize();
295 auto srcSize = src->GetSuitSize();
296 LOGD("Get Size for event: %{public}d, share id: %{public}s. dest: w: %{public}lf, h: %{public}lf; "
297 "srcSize: w: %{public}lf, h: %{public}lf",
298 event, shareId_.c_str(), destSize.Width(), destSize.Height(), srcSize.Width(), srcSize.Height());
299
300 // add width shared transition
301 auto& propertyMap = option.GetFloatPropertyAnimation();
302 auto widthIter = propertyMap.find(PropertyAnimatableType::PROPERTY_WIDTH);
303 if (((isLazy && autoWidth_) || (widthIter == propertyMap.end())) && !NearEqual(destSize.Width(), srcSize.Width())) {
304 auto widthAnimation =
305 AceType::MakeRefPtr<CurveAnimation<float>>(srcSize.Width(), destSize.Width(), Curves::FRICTION);
306 option.SetPropertyAnimationFloat(PropertyAnimatableType::PROPERTY_WIDTH, widthAnimation);
307 autoWidth_ = true;
308 LOGD("Create shared exchange animation for width. share id: %{public}s", shareId_.c_str());
309 }
310
311 // add scaleY shared transition
312 auto heightIter = propertyMap.find(PropertyAnimatableType::PROPERTY_HEIGHT);
313 if (((isLazy && autoHeight_) || (heightIter == propertyMap.end())) &&
314 !NearEqual(destSize.Height(), srcSize.Height())) {
315 auto heightAnimation =
316 AceType::MakeRefPtr<CurveAnimation<float>>(srcSize.Height(), destSize.Height(), Curves::FRICTION);
317 option.SetPropertyAnimationFloat(PropertyAnimatableType::PROPERTY_HEIGHT, heightAnimation);
318 LOGD("Create shared exchange animation for height. share id: %{public}s", shareId_.c_str());
319 autoHeight_ = true;
320 }
321 return true;
322 }
323
CreateOpacityAnimation(TweenOption & option,TransitionEvent event,bool isLazy)324 bool SharedTransitionExchange::CreateOpacityAnimation(TweenOption& option, TransitionEvent event, bool isLazy)
325 {
326 auto src = src_.Upgrade();
327 auto dest = dest_.Upgrade();
328 if (!dest || !src) {
329 LOGE("Create exchange animation failed. dest or src is null. event: %{public}d, share id: %{public}s", event,
330 shareId_.c_str());
331 return false;
332 }
333 auto destOpacity = dest->GetOpacity();
334 auto srcOpacity = src->GetOpacity();
335
336 LOGD("Get Opacity for event: %{public}d, share id: %{public}s. dest: %{public}f; src: %{public}f", event,
337 shareId_.c_str(), destOpacity, srcOpacity);
338
339 if (!NearEqual(destOpacity, srcOpacity) && !option.GetOpacityAnimation()) {
340 auto opacityAnimation = AceType::MakeRefPtr<CurveAnimation<float>>(srcOpacity, destOpacity, Curves::FRICTION);
341 option.SetOpacityAnimation(opacityAnimation);
342 }
343 return true;
344 }
345
CreateAnimation(TweenOption & option,TransitionEvent event,bool isLazy)346 bool SharedTransitionExchange::CreateAnimation(TweenOption& option, TransitionEvent event, bool isLazy)
347 {
348 auto src = src_.Upgrade();
349 auto dest = dest_.Upgrade();
350 if (!dest || !src) {
351 LOGE("Create exchange animation failed. dest or src is null. event: %{public}d, share id: %{public}s", event,
352 shareId_.c_str());
353 return false;
354 }
355 if (!isLazy) {
356 autoTranslate_ = false;
357 autoWidth_ = false;
358 autoHeight_ = false;
359 }
360
361 // add translate shared transition
362 if (!CreateTranslateAnimation(option, event, isLazy)) {
363 return false;
364 }
365 if (!CreateSizeAnimation(option, event, isLazy)) {
366 return false;
367 }
368 if (!CreateOpacityAnimation(option, event, isLazy)) {
369 return false;
370 }
371 AddLazyLoadCallback(event);
372 return true;
373 }
374
ApplyAnimation(RefPtr<OverlayElement> & overlay,RefPtr<Animator> & controller,TweenOption & option,TransitionEvent event)375 bool SharedTransitionExchange::ApplyAnimation(RefPtr<OverlayElement>& overlay, RefPtr<Animator>& controller,
376 TweenOption& option, TransitionEvent event)
377 {
378 if (!SharedTransitionEffect::ApplyAnimation(overlay, controller, option, event)) {
379 return false;
380 }
381 Offset ticket;
382 if (!CheckIn(event, src_, ticket)) {
383 LOGE(
384 "Apply exchange failed. check in failed. event: %{public}d, share id: %{public}s", event, shareId_.c_str());
385 return false;
386 }
387 return TakeOff(event, overlay, src_, ticket, option);
388 }
389
Allow(TransitionEvent event)390 bool SharedTransitionStatic::Allow(TransitionEvent event)
391 {
392 auto current = GetCurrentSharedElement().Upgrade();
393 if (!current) {
394 LOGD("Check allow static failed. current null. event: %{public}d, share id: %{public}s", event,
395 shareId_.c_str());
396 return false;
397 }
398 bool allow = false;
399 if (event == TransitionEvent::PUSH_START) {
400 // In Push Scene, dest means Enter and Source means Exit
401 allow = current->IsEnablePushEnter();
402 } else if (event == TransitionEvent::POP_START) {
403 // In Pop Scene, dest means Enter and Source means Exit
404 allow = current->IsEnablePopEnter();
405 }
406 LOGD("Static Allow status: %{public}d, event: %{public}d, share id: %{public}s", allow, event, shareId_.c_str());
407 return allow;
408 }
409
CreateAnimation(TweenOption & option,TransitionEvent event,bool isLazy)410 bool SharedTransitionStatic::CreateAnimation(TweenOption& option, TransitionEvent event, bool isLazy)
411 {
412 if (src_.Invalid()) {
413 // anchor appearing, opacity 0 to 1
414 auto opacityAnimation = option.GetOpacityAnimation();
415 if (!opacityAnimation) {
416 TransitionTweenOptionFactory::CreateSharedTweenOption(
417 SharedTransitionEffectType::SHARED_EFFECT_STATIC, option);
418 }
419 } else {
420 // anchor disappearing, opacity 1 to 0
421 if (!option.GetOpacityAnimation()) {
422 auto animation = AceType::MakeRefPtr<CurveAnimation<float>>(1.0f, 0.0f, option.GetCurve());
423 option.SetOpacityAnimation(animation);
424 }
425 }
426 return true;
427 }
428
ApplyAnimation(RefPtr<OverlayElement> & overlay,RefPtr<Animator> & controller,TweenOption & option,TransitionEvent event)429 bool SharedTransitionStatic::ApplyAnimation(
430 RefPtr<OverlayElement>& overlay, RefPtr<Animator>& controller, TweenOption& option, TransitionEvent event)
431 {
432 if (!SharedTransitionEffect::ApplyAnimation(overlay, controller, option, event)) {
433 return false;
434 }
435 Offset ticket;
436 // the dest page and source page elements are in effect
437 auto current = GetCurrentSharedElement();
438 if (!CheckIn(event, current, ticket)) {
439 LOGE("Apply static fail. check in failed. event: %{public}d, share id: %{public}s", event, shareId_.c_str());
440 return false;
441 }
442 AddLazyLoadCallback();
443 return TakeOff(event, overlay, current, ticket, option);
444 }
445
AddLazyLoadCallback()446 void SharedTransitionStatic::AddLazyLoadCallback()
447 {
448 auto current = GetCurrentSharedElement().Upgrade();
449 if (!current) {
450 LOGE("Add Lazy load Callback failed. current is null.");
451 return;
452 }
453 current->SetSizeModified([effectWeak = WeakClaim(this)]() {
454 auto effect = effectWeak.Upgrade();
455 if (!effect) {
456 LOGE("Fix static shared element position failed. effect is null");
457 return;
458 }
459 auto tweenSeatElement = effect->tweenSeatElement_.Upgrade();
460 if (!tweenSeatElement) {
461 LOGE("Fix static shared element position failed. tween element is null");
462 return;
463 }
464 auto positionedElement =
465 AceType::DynamicCast<PositionedElement>(tweenSeatElement->GetElementParent().Upgrade());
466 if (!positionedElement) {
467 LOGE("Fix static shared element position failed. positioned element is null");
468 return;
469 }
470 auto positionedRender = AceType::DynamicCast<RenderPositioned>(positionedElement->GetRenderNode());
471 if (!positionedRender) {
472 LOGE("Fix static shared element position failed. positioned render is null");
473 return;
474 }
475 auto dest = effect->currentWorking_.Upgrade();
476 if (!dest) {
477 return;
478 }
479 auto offset = dest->GetGlobalOffset();
480 positionedRender->SetTop(Dimension(offset.GetY(), DimensionUnit::PX));
481 positionedRender->SetLeft(Dimension(offset.GetX(), DimensionUnit::PX));
482 });
483 }
484
485 } // namespace OHOS::Ace
486