• 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/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