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