• 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/components/page_transition/page_transition_element.h"
17 
18 #include "base/utils/system_properties.h"
19 #include "core/animation/shared_transition_controller.h"
20 #include "core/components/clip/clip_component.h"
21 #include "core/components/clip/clip_element.h"
22 #include "core/components/clip/render_clip.h"
23 #include "core/components/page/page_element.h"
24 #include "core/components/page_transition/page_transition_component.h"
25 #include "core/components/transition/transition_component.h"
26 
27 namespace OHOS::Ace {
28 
29 namespace {
30 
31 constexpr int32_t CHILDREN_SIZE_WHEN_SPLIT = 2;
32 
GetOptionType(bool hasSharedTransition,TransitionDirection direction)33 TransitionOptionType GetOptionType(bool hasSharedTransition, TransitionDirection direction)
34 {
35     if (hasSharedTransition) {
36         if (direction == TransitionDirection::TRANSITION_IN) {
37             return TransitionOptionType::TRANSITION_SHARED_IN;
38         } else {
39             return TransitionOptionType::TRANSITION_SHARED_OUT;
40         }
41     } else {
42         if (direction == TransitionDirection::TRANSITION_IN) {
43             return TransitionOptionType::TRANSITION_IN;
44         } else {
45             return TransitionOptionType::TRANSITION_OUT;
46         }
47     }
48 }
49 
50 } // namespace
51 
Update()52 void PageTransitionElement::Update()
53 {
54     StackElement::Update();
55     UpdateTransitionOption();
56 }
57 
PerformBuild()58 void PageTransitionElement::PerformBuild()
59 {
60     // PageTransitionElement only have two children. one is content, the other is background.
61     if (!children_.empty()) {
62         LOGE("perform build failed. not empty children. size: %{public}u, skip perform build.",
63             static_cast<int32_t>(children_.size()));
64         return;
65     }
66 
67     auto pageTransitionComponent = AceType::DynamicCast<PageTransitionComponent>(component_);
68     if (!pageTransitionComponent) {
69         LOGE("PageTransitionElement::PerformBuild: get PageTransitionComponent failed!");
70         return;
71     }
72     SetElementId(pageTransitionComponent->GetElementId());
73 
74     if (!controller_) {
75         controller_ = CREATE_ANIMATOR(context_);
76     }
77     UpdateTransitionOption();
78 
79     if (pageTransitionComponent->GetSeparation()) {
80         BuildSeparatedChild(pageTransitionComponent);
81     } else {
82         BuildCombinedChild(pageTransitionComponent);
83     }
84     SetTransitionController();
85 }
86 
SetTransitionController()87 void PageTransitionElement::SetTransitionController()
88 {
89     if (!controller_) {
90         LOGE("set transition controller failed. controller is null");
91         return;
92     }
93 
94     // stop controller first.
95     if (!controller_->IsStopped()) {
96         controller_->Stop();
97     }
98 
99     if (contentTransition_) {
100         auto contentController = contentTransition_->GetController();
101         if (contentController && (!contentController->IsStopped())) {
102             contentController->Stop();
103         }
104         contentTransition_->SetController(controller_);
105     }
106     if (frontDecorationTransition_) {
107         auto frontController = frontDecorationTransition_->GetController();
108         if (frontController && (!frontController->IsStopped())) {
109             frontController->Stop();
110         }
111         frontDecorationTransition_->SetController(controller_);
112     }
113     if (backgroundTransition_) {
114         auto backgroundController = backgroundTransition_->GetController();
115         if (backgroundController && (!backgroundController->IsStopped())) {
116             backgroundController->Stop();
117         }
118         backgroundTransition_->SetController(controller_);
119     }
120 }
121 
SetTransitionDirection(TransitionEvent event,TransitionDirection direction)122 void PageTransitionElement::SetTransitionDirection(TransitionEvent event, TransitionDirection direction)
123 {
124     if (!controller_) {
125         LOGE("set transition direction failed. controller is empty.");
126         return;
127     }
128     auto context = context_.Upgrade();
129     if (!context) {
130         LOGE("set transition direction failed. context is empty.");
131         return;
132     }
133     auto sharedController = context->GetSharedTransitionController();
134     if (!sharedController) {
135         LOGE("set transition direction failed. shared controller is null.");
136         return;
137     }
138     controller_->ClearInterpolators();
139 
140     // stop controller first.
141     if (!controller_->IsStopped()) {
142         controller_->Stop();
143     }
144     TransitionOptionType optionType;
145     auto deviceType = SystemProperties::GetDeviceType();
146     if (deviceType == DeviceType::TV) {
147         // no shared transition UI standard on tv, just use default page transition parameters
148         optionType = GetOptionType(false, direction);
149     } else {
150         bool hasShared = sharedController->HasSharedTransition(event);
151         optionType = GetOptionType(hasShared, direction);
152     }
153     if (contentTransition_) {
154         contentTransition_->SwitchTransitionOption(optionType);
155     }
156     if (frontDecorationTransition_) {
157         frontDecorationTransition_->SwitchTransitionOption(optionType);
158     }
159     if (backgroundTransition_) {
160         backgroundTransition_->SwitchTransitionOption(optionType);
161     }
162     if (context && context->GetIsDeclarative()) {
163         if (floatAnimation_) {
164             controller_->AddInterpolator(std::move(floatAnimation_));
165             controller_->SetAllowRunningAsynchronously(false);
166         } else {
167             controller_->SetAllowRunningAsynchronously(true);
168         }
169     }
170 }
171 
GetTransitionController() const172 const RefPtr<Animator>& PageTransitionElement::GetTransitionController() const
173 {
174     return controller_;
175 }
176 
UpdateTransitionOption()177 void PageTransitionElement::UpdateTransitionOption()
178 {
179     if (!component_) {
180         LOGE("update transition option failed. component is null.");
181         return;
182     }
183     auto transitionComponent = AceType::DynamicCast<PageTransitionComponent>(component_);
184     if (!transitionComponent) {
185         LOGE("update transition option failed. transition is null.");
186         return;
187     }
188     isRightToLeft_ = transitionComponent->GetTextDirection() == TextDirection::RTL;
189 
190     contentInOption_ = transitionComponent->GetContentTransitionInOption();
191     contentOutOption_ = transitionComponent->GetContentTransitionOutOption();
192     pageTransitions_ = transitionComponent->GetPageTransitions();
193 
194     sharedInOption_ = contentInOption_;
195     sharedOutOption_ = contentOutOption_;
196     isCustomOptionIn_ = contentInOption_.IsValid();
197     isCustomOptionOut_ = contentOutOption_.IsValid();
198 }
199 
GetTransitionElement(const RefPtr<Element> & element)200 RefPtr<PageTransitionElement> PageTransitionElement::GetTransitionElement(const RefPtr<Element>& element)
201 {
202     // first try with page element.
203     RefPtr<PageElement> page = AceType::DynamicCast<PageElement>(element);
204     if (!page) {
205         return nullptr;
206     }
207     auto child = page->GetFirstChild();
208     return AceType::DynamicCast<PageTransitionElement>(child);
209 }
210 
SetTouchable(bool enable)211 void PageTransitionElement::SetTouchable(bool enable)
212 {
213     if (backgroundTransition_) {
214         backgroundTransition_->SetTouchable(enable);
215     }
216 
217     if (contentTransition_) {
218         contentTransition_->SetTouchable(enable);
219     }
220 }
221 
InitTransitionClip()222 void PageTransitionElement::InitTransitionClip()
223 {
224     if (!contentTransition_) {
225         LOGE("InitTransitionClip failed, content transition is null.");
226         return;
227     }
228     auto clipElement = contentTransition_->GetContentElement();
229     if (AceType::InstanceOf<ClipElement>(clipElement)) {
230         RefPtr<RenderClip> clipRender = DynamicCast<RenderClip>(clipElement->GetRenderNode());
231         if (clipRender) {
232             clipRender->SetWidth(0.0);
233             clipRender->SetHeight(0.0);
234         }
235     }
236 }
237 
InitController(TransitionDirection direction,TransitionEvent event)238 void PageTransitionElement::InitController(TransitionDirection direction, TransitionEvent event)
239 {
240     if (!controller_) {
241         LOGE("init controller failed. controller is null.");
242         return;
243     }
244     if (event == TransitionEvent::PUSH_END || event == TransitionEvent::POP_END) {
245         LOGE("init controller failed. event can not be handled. event: %{public}d", event);
246         return;
247     }
248     SetTouchable(false);
249     if ((direction == TransitionDirection::TRANSITION_OUT) && (event == TransitionEvent::PUSH_START)) {
250         return;
251     }
252     if ((direction == TransitionDirection::TRANSITION_IN) && (event == TransitionEvent::POP_START)) {
253         return;
254     }
255     auto weak = AceType::WeakClaim(this);
256     controller_->AddStopListener([weak]() {
257         auto transition = weak.Upgrade();
258         if (transition) {
259             transition->SetTouchable(true);
260         }
261     });
262 }
263 
SetWrapHidden(bool hidden)264 void PageTransitionElement::SetWrapHidden(bool hidden)
265 {
266     if (contentTransition_) {
267         contentTransition_->SetWrapHidden(hidden);
268     }
269 
270     if (backgroundTransition_) {
271         backgroundTransition_->SetWrapHidden(hidden);
272     }
273 }
274 
AddPreFlush()275 void PageTransitionElement::AddPreFlush()
276 {
277     if (contentTransition_) {
278         contentTransition_->AddPreFlush();
279     }
280 
281     if (backgroundTransition_) {
282         backgroundTransition_->AddPreFlush();
283     }
284 }
285 
SkipPostFlush()286 void PageTransitionElement::SkipPostFlush()
287 {
288     if (contentTransition_) {
289         contentTransition_->SkipPostFlush();
290     }
291 
292     if (backgroundTransition_) {
293         backgroundTransition_->SkipPostFlush();
294     }
295 }
296 
GetContentElement() const297 RefPtr<Element> PageTransitionElement::GetContentElement() const
298 {
299     if (!contentTransition_) {
300         LOGE("get content element failed. content tween is null.");
301         return nullptr;
302     }
303     auto element = contentTransition_->GetContentElement();
304     if (AceType::InstanceOf<ClipElement>(element)) {
305         auto transition = DynamicCast<TransitionElement>(element->GetFirstChild());
306         if (transition) {
307             auto frontDecorationBox = transition->GetContentElement();
308             if (frontDecorationBox) {
309                 return frontDecorationBox->GetFirstChild();
310             }
311         }
312         return transition;
313     }
314     return element;
315 }
316 
LoadTransition()317 void PageTransitionElement::LoadTransition()
318 {
319     auto pageElement = GetPageElement();
320     if (!pageElement) {
321         return;
322     }
323     auto componentUpdated = pageElement->CallPageTransitionFunction();
324     // save origin component
325     auto componentOrigin = component_;
326     component_ = componentUpdated;
327     // update with updated component
328     UpdateTransitionOption();
329     // restore origin component
330     component_ = componentOrigin;
331 }
332 
ResetPageTransitionAnimation()333 void PageTransitionElement::ResetPageTransitionAnimation()
334 {
335     if (contentTransition_) {
336         contentTransition_->ResetPageTransitionAnimation();
337     }
338 }
339 
SetTransition(DeviceType deviceType,TransitionEvent event,TransitionDirection direction,const RRect & rrect)340 void PageTransitionElement::SetTransition(
341     DeviceType deviceType, TransitionEvent event, TransitionDirection direction, const RRect& rrect)
342 {
343     auto tweenOption =
344         TransitionTweenOptionFactory::CreateTransitionTweenOption(deviceType, event, isRightToLeft_, rrect, context_);
345     if (!tweenOption) {
346         LOGE("TransitionTweenOption is null.");
347         return;
348     }
349     bool isSetOutOption = false;
350     int32_t duration = tweenOption->GetTransitionContentInOption().GetDuration();
351     int32_t delay = 0;
352     if (direction == TransitionDirection::TRANSITION_OUT) {
353         if (isCustomOptionOut_) {
354             if (contentOutOption_.HasDurationChanged()) {
355                 duration = contentOutOption_.GetDuration();
356             }
357             isSetOutOption = true;
358         } else {
359             contentOutOption_ = tweenOption->GetTransitionContentOutOption();
360             sharedOutOption_ = tweenOption->GetSharedOutOption();
361         }
362     }
363     if (direction == TransitionDirection::TRANSITION_IN) {
364         if (isCustomOptionIn_) {
365             if (contentInOption_.HasDurationChanged()) {
366                 duration = contentInOption_.GetDuration();
367             }
368         } else {
369             contentInOption_ = tweenOption->GetTransitionContentInOption();
370             sharedInOption_ = tweenOption->GetSharedInOption();
371         }
372     }
373     auto context = GetContext().Upgrade();
374     if (context && context->GetIsDeclarative()) {
375         auto pageTransition = GetCurrentPageTransition(event, direction_);
376         isCustomOption_ = false;
377         isSetOutOption = true;
378         if (direction == TransitionDirection::TRANSITION_OUT) {
379             if (pageTransition) {
380                 contentOutOption_ = ProcessPageTransition(pageTransition, event);
381                 if (contentOutOption_.HasDurationChanged()) {
382                     duration = contentOutOption_.GetDuration();
383                 }
384                 delay = contentOutOption_.GetDelay();
385                 isCustomOption_ = true;
386                 sharedOutOption_ = contentOutOption_;
387             }
388         } else {
389             if (pageTransition) {
390                 contentInOption_ = ProcessPageTransition(pageTransition, event);
391                 if (contentInOption_.HasDurationChanged()) {
392                     duration = contentInOption_.GetDuration();
393                 }
394                 delay = contentInOption_.GetDelay();
395                 isCustomOption_ = true;
396                 sharedInOption_ = contentInOption_;
397             }
398         }
399     }
400     if (controller_) {
401         controller_->SetDuration(duration);
402         if (context && context->GetIsDeclarative() && delay >= 0) {
403             controller_->SetStartDelay(delay);
404         }
405     }
406     if (contentTransition_) {
407         contentTransition_->SetTransition(contentInOption_, contentOutOption_);
408         contentTransition_->SetSharedTransition(sharedInOption_, sharedOutOption_);
409     }
410     if (frontDecorationTransition_ && !isSetOutOption) {
411         // do not need option in.
412         TweenOption optionIn;
413         frontDecorationTransition_->SetTransition(optionIn, tweenOption->GetTransitionFrontDecorationOption());
414         frontDecorationTransition_->SetSharedTransition(
415             optionIn, tweenOption->GetSharedTransitionFrontDecorationOption());
416     }
417     if (backgroundTransition_) {
418         backgroundTransition_->SetTransition(
419             tweenOption->GetTransitionBackgroundInOption(), tweenOption->GetTransitionBackgroundOutOption());
420     }
421 }
422 
BuildCombinedChild(const RefPtr<StackComponent> & component)423 void PageTransitionElement::BuildCombinedChild(const RefPtr<StackComponent>& component)
424 {
425     auto pageTransitionComponent = AceType::DynamicCast<PageTransitionComponent>(component);
426     if (!pageTransitionComponent) {
427         LOGE("Get PageTransitionComponent failed!");
428         return;
429     }
430     // create transition for content
431     auto box = AceType::MakeRefPtr<BoxComponent>();
432     Component::MergeRSNode(box);
433     auto front = AceType::MakeRefPtr<Decoration>();
434     front->SetBackgroundColor(Color::FromRGBO(0, 0, 0, 0.0));
435     box->SetFrontDecoration(front);
436     box->SetChild(pageTransitionComponent->GetContent());
437     auto transition = AceType::MakeRefPtr<TransitionComponent>(
438         TransitionComponent::AllocTransitionComponentId(), "frontDecoration_transition", box);
439 
440     auto clip = AceType::MakeRefPtr<ClipComponent>(transition);
441     Component::MergeRSNode(clip);
442     auto contentTransitionComponent = AceType::MakeRefPtr<TransitionComponent>(
443         TransitionComponent::AllocTransitionComponentId(), "page_transition_content", clip);
444 
445     // add transition for content
446     pageTransitionComponent->AppendChild(contentTransitionComponent);
447     StackElement::PerformBuild();
448 
449     if (children_.size() != 1) {
450         LOGE("the children size is error.");
451         return;
452     }
453     auto childIter = children_.begin();
454     auto child = *childIter;
455 
456     // child for content.
457     contentTransition_ = AceType::DynamicCast<TransitionElement>(child);
458     auto frontElement = contentTransition_->GetContentElement();
459     if (frontElement) {
460         frontDecorationTransition_ = DynamicCast<TransitionElement>(frontElement->GetFirstChild());
461     }
462 }
463 
BuildSeparatedChild(const RefPtr<StackComponent> & component)464 void PageTransitionElement::BuildSeparatedChild(const RefPtr<StackComponent>& component)
465 {
466     auto pageTransitionComponent = AceType::DynamicCast<PageTransitionComponent>(component);
467     if (!pageTransitionComponent) {
468         LOGE("BuildSeparatedChild : get PageTransitionComponent failed!");
469         return;
470     }
471     // add transition for background
472     pageTransitionComponent->AppendChild(
473         AceType::MakeRefPtr<TransitionComponent>(TransitionComponent::AllocTransitionComponentId(),
474             "page_transition_background", pageTransitionComponent->GetBackground()));
475 
476     // create transition for content
477     auto contentTransitionComponent =
478         AceType::MakeRefPtr<TransitionComponent>(TransitionComponent::AllocTransitionComponentId(),
479             "page_transition_content", pageTransitionComponent->GetContent());
480 
481     // add transition for content
482     pageTransitionComponent->AppendChild(contentTransitionComponent);
483     StackElement::PerformBuild();
484 
485     if (children_.size() != CHILDREN_SIZE_WHEN_SPLIT) {
486         LOGE("the children size is error.");
487         return;
488     }
489     auto childIter = children_.begin();
490     auto child = *childIter;
491 
492     // child for background
493     backgroundTransition_ = AceType::DynamicCast<TransitionElement>(child);
494     child = *(++childIter);
495 
496     // child for content.
497     contentTransition_ = AceType::DynamicCast<TransitionElement>(child);
498 }
499 
GetContentTransitionElement() const500 const RefPtr<TransitionElement>& PageTransitionElement::GetContentTransitionElement() const
501 {
502     return contentTransition_;
503 }
504 
GetBackgroundTransitionElement() const505 const RefPtr<TransitionElement>& PageTransitionElement::GetBackgroundTransitionElement() const
506 {
507     return backgroundTransition_;
508 }
509 
ProcessPageTransition(const RefPtr<PageTransition> & pageTransition,TransitionEvent event)510 TweenOption PageTransitionElement::ProcessPageTransition(
511     const RefPtr<PageTransition>& pageTransition, TransitionEvent event)
512 {
513     auto tweenOption = pageTransition->GetTweenOption();
514     // 1.SlideEffect
515     auto transitionDeclarativeTweenOption = TransitionDeclarativeTweenOption(isRightToLeft_, GetContext());
516     transitionDeclarativeTweenOption.CreateSlideEffectAnimation(
517         tweenOption, pageTransition->GetSlideEffect(), pageTransition->GetType(), direction_);
518     // 2. callback
519     auto onExitHandler = pageTransition->GetOnExitHandler();
520     auto onEnterHandler = pageTransition->GetOnEnterHandler();
521     RouteType type = RouteType::PUSH;
522     if (event == TransitionEvent::POP_START || event == TransitionEvent::POP_END) {
523         type = RouteType::POP;
524     }
525     if (onExitHandler || onEnterHandler) {
526         floatAnimation_ = AceType::MakeRefPtr<CurveAnimation<float>>(0.0f, 1.0f, pageTransition->GetCurve());
527         if (onExitHandler) {
528             floatAnimation_->AddListener(
529                 [type, onExitHandler](const float& progress) { onExitHandler(type, progress); });
530         }
531         if (onEnterHandler) {
532             floatAnimation_->AddListener(
533                 [type, onEnterHandler](const float& progress) { onEnterHandler(type, progress); });
534         }
535     }
536     // 3. delay curve
537     return tweenOption;
538 }
539 
GetCurrentPageTransition(TransitionEvent event,TransitionDirection direction) const540 RefPtr<PageTransition> PageTransitionElement::GetCurrentPageTransition(
541     TransitionEvent event, TransitionDirection direction) const
542 {
543     if (pageTransitions_.empty()) {
544         return nullptr;
545     }
546     auto type = GetPageTransitionType(event, direction);
547     auto pos = pageTransitions_.find(type);
548     if (pos != pageTransitions_.end()) {
549         return pos->second;
550     }
551 
552     if (direction == TransitionDirection::TRANSITION_IN) {
553         type = PageTransitionType::ENTER;
554     } else {
555         type = PageTransitionType::EXIT;
556     }
557     pos = pageTransitions_.find(type);
558     if (pos != pageTransitions_.end()) {
559         return pos->second;
560     }
561     return nullptr;
562 }
563 
GetPageTransitionType(TransitionEvent event,TransitionDirection direction)564 PageTransitionType PageTransitionElement::GetPageTransitionType(TransitionEvent event, TransitionDirection direction)
565 {
566     if (direction == TransitionDirection::TRANSITION_IN) {
567         if (event == TransitionEvent::POP_START) {
568             return PageTransitionType::ENTER_POP;
569         } else {
570             return PageTransitionType::ENTER_PUSH;
571         }
572     } else {
573         if (event == TransitionEvent::POP_START) {
574             return PageTransitionType::EXIT_POP;
575         } else {
576             return PageTransitionType::EXIT_PUSH;
577         }
578     }
579 }
580 
581 } // namespace OHOS::Ace
582