• 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 
73     if (!controller_) {
74         LOGD("create animator.");
75         controller_ = AceType::MakeRefPtr<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         LOGD("set content transition option type: %{public}d", optionType);
155         contentTransition_->SwitchTransitionOption(optionType);
156     }
157     if (frontDecorationTransition_) {
158         LOGD("set front transition option type: %{public}d", direction);
159         frontDecorationTransition_->SwitchTransitionOption(optionType);
160     }
161     if (backgroundTransition_) {
162         LOGD("set background transition option type: %{public}d", optionType);
163         backgroundTransition_->SwitchTransitionOption(optionType);
164     }
165     if (context && context->GetIsDeclarative()) {
166         if (floatAnimation_) {
167             controller_->AddInterpolator(std::move(floatAnimation_));
168             controller_->SetAllowRunningAsynchronously(false);
169         } else {
170             controller_->SetAllowRunningAsynchronously(true);
171         }
172     }
173 }
174 
GetTransitionController() const175 const RefPtr<Animator>& PageTransitionElement::GetTransitionController() const
176 {
177     return controller_;
178 }
179 
UpdateTransitionOption()180 void PageTransitionElement::UpdateTransitionOption()
181 {
182     if (!component_) {
183         LOGE("update transition option failed. component is null.");
184         return;
185     }
186     auto transitionComponent = AceType::DynamicCast<PageTransitionComponent>(component_);
187     if (!transitionComponent) {
188         LOGE("update transition option failed. transition is null.");
189         return;
190     }
191     isRightToLeft_ = transitionComponent->GetTextDirection() == TextDirection::RTL;
192 
193     LOGI("Use user-defined transition parameters.");
194     contentInOption_ = transitionComponent->GetContentTransitionInOption();
195     contentOutOption_ = transitionComponent->GetContentTransitionOutOption();
196     pageTransitions_ = transitionComponent->GetPageTransitions();
197 
198     sharedInOption_ = contentInOption_;
199     sharedOutOption_ = contentOutOption_;
200     isCustomOptionIn_ = contentInOption_.IsValid();
201     isCustomOptionOut_ = contentOutOption_.IsValid();
202 }
203 
GetTransitionElement(const RefPtr<Element> & element)204 RefPtr<PageTransitionElement> PageTransitionElement::GetTransitionElement(const RefPtr<Element>& element)
205 {
206     // first try with page element.
207     RefPtr<PageElement> page = AceType::DynamicCast<PageElement>(element);
208     if (!page) {
209         return nullptr;
210     }
211     LOGD("try to get transition element from page element.");
212     auto child = page->GetFirstChild();
213     return AceType::DynamicCast<PageTransitionElement>(child);
214 }
215 
SetTouchable(bool enable)216 void PageTransitionElement::SetTouchable(bool enable)
217 {
218     if (backgroundTransition_) {
219         backgroundTransition_->SetTouchable(enable);
220     }
221 
222     if (contentTransition_) {
223         contentTransition_->SetTouchable(enable);
224     }
225 }
226 
InitTransitionClip()227 void PageTransitionElement::InitTransitionClip()
228 {
229     if (!contentTransition_) {
230         LOGE("InitTransitionClip failed, content transition is null.");
231         return;
232     }
233     auto clipElement = contentTransition_->GetContentElement();
234     if (AceType::InstanceOf<ClipElement>(clipElement)) {
235         RefPtr<RenderClip> clipRender = DynamicCast<RenderClip>(clipElement->GetRenderNode());
236         if (clipRender) {
237             clipRender->SetWidth(0.0);
238             clipRender->SetHeight(0.0);
239         }
240     }
241 }
242 
InitController(TransitionDirection direction,TransitionEvent event)243 void PageTransitionElement::InitController(TransitionDirection direction, TransitionEvent event)
244 {
245     if (!controller_) {
246         LOGE("init controller failed. controller is null.");
247         return;
248     }
249     if (event == TransitionEvent::PUSH_END || event == TransitionEvent::POP_END) {
250         LOGE("init controller failed. event can not be handled. event: %{public}d", event);
251         return;
252     }
253     SetTouchable(false);
254     if ((direction == TransitionDirection::TRANSITION_OUT) && (event == TransitionEvent::PUSH_START)) {
255         LOGD("No need to make it touchable after transition done, because page will not on the top of the stage.");
256         return;
257     }
258     if ((direction == TransitionDirection::TRANSITION_IN) && (event == TransitionEvent::POP_START)) {
259         LOGD("No need to make it touchable after transition done, because page will be destroyed.");
260         return;
261     }
262     auto weak = AceType::WeakClaim(this);
263     controller_->AddStopListener([weak]() {
264         LOGD("transition complete, prepare to clear it");
265         auto transition = weak.Upgrade();
266         if (transition) {
267             LOGD("transition complete, clear it");
268             transition->SetTouchable(true);
269         }
270     });
271 }
272 
SetWrapHidden(bool hidden)273 void PageTransitionElement::SetWrapHidden(bool hidden)
274 {
275     if (contentTransition_) {
276         contentTransition_->SetWrapHidden(hidden);
277     }
278 
279     if (backgroundTransition_) {
280         backgroundTransition_->SetWrapHidden(hidden);
281     }
282 }
283 
AddPreFlush()284 void PageTransitionElement::AddPreFlush()
285 {
286     if (contentTransition_) {
287         contentTransition_->AddPreFlush();
288     }
289 
290     if (backgroundTransition_) {
291         backgroundTransition_->AddPreFlush();
292     }
293 }
294 
SkipPostFlush()295 void PageTransitionElement::SkipPostFlush()
296 {
297     if (contentTransition_) {
298         contentTransition_->SkipPostFlush();
299     }
300 
301     if (backgroundTransition_) {
302         backgroundTransition_->SkipPostFlush();
303     }
304 }
305 
GetContentElement() const306 RefPtr<Element> PageTransitionElement::GetContentElement() const
307 {
308     if (!contentTransition_) {
309         LOGE("get content element failed. content tween is null.");
310         return nullptr;
311     }
312     auto element = contentTransition_->GetContentElement();
313     if (AceType::InstanceOf<ClipElement>(element)) {
314         auto transition = DynamicCast<TransitionElement>(element->GetFirstChild());
315         if (transition) {
316             auto frontDecorationBox = transition->GetContentElement();
317             if (frontDecorationBox) {
318                 return frontDecorationBox->GetFirstChild();
319             }
320         }
321         return transition;
322     }
323     return element;
324 }
325 
LoadTransition()326 void PageTransitionElement::LoadTransition()
327 {
328     auto pageElement = GetPageElement();
329     if (!pageElement) {
330         return;
331     }
332     auto componentUpdated = pageElement->CallPageTransitionFunction();
333     // save origin component
334     auto componentOrigin = component_;
335     component_ = componentUpdated;
336     // update with updated component
337     UpdateTransitionOption();
338     // restore origin component
339     component_ = componentOrigin;
340 }
341 
ResetPageTransitionAnimation()342 void PageTransitionElement::ResetPageTransitionAnimation()
343 {
344     if (contentTransition_) {
345         contentTransition_->ResetPageTransitionAnimation();
346     }
347 }
348 
SetTransition(DeviceType deviceType,TransitionEvent event,TransitionDirection direction,const RRect & rrect)349 void PageTransitionElement::SetTransition(
350     DeviceType deviceType, TransitionEvent event, TransitionDirection direction, const RRect& rrect)
351 {
352     auto tweenOption =
353         TransitionTweenOptionFactory::CreateTransitionTweenOption(deviceType, event, isRightToLeft_, rrect, context_);
354     if (!tweenOption) {
355         LOGE("TransitionTweenOption is null.");
356         return;
357     }
358     bool isSetOutOption = false;
359     int32_t duration = tweenOption->GetTransitionContentInOption().GetDuration();
360     int32_t delay = 0;
361     if (direction == TransitionDirection::TRANSITION_OUT) {
362         if (isCustomOptionOut_) {
363             if (contentOutOption_.HasDurationChanged()) {
364                 duration = contentOutOption_.GetDuration();
365             }
366             isSetOutOption = true;
367         } else {
368             contentOutOption_ = tweenOption->GetTransitionContentOutOption();
369             sharedOutOption_ = tweenOption->GetSharedOutOption();
370         }
371     }
372     if (direction == TransitionDirection::TRANSITION_IN) {
373         if (isCustomOptionIn_) {
374             if (contentInOption_.HasDurationChanged()) {
375                 duration = contentInOption_.GetDuration();
376             }
377         } else {
378             contentInOption_ = tweenOption->GetTransitionContentInOption();
379             sharedInOption_ = tweenOption->GetSharedInOption();
380         }
381     }
382     auto context = GetContext().Upgrade();
383     if (context && context->GetIsDeclarative()) {
384         auto pageTransition = GetCurrentPageTransition(event, direction_);
385         isCustomOption_ = false;
386         isSetOutOption = true;
387         if (direction == TransitionDirection::TRANSITION_OUT) {
388             if (pageTransition) {
389                 contentOutOption_ = ProcessPageTransition(pageTransition, event);
390                 if (contentOutOption_.HasDurationChanged()) {
391                     duration = contentOutOption_.GetDuration();
392                 }
393                 delay = contentOutOption_.GetDelay();
394                 isCustomOption_ = true;
395                 sharedOutOption_ = contentOutOption_;
396             }
397         } else {
398             if (pageTransition) {
399                 contentInOption_ = ProcessPageTransition(pageTransition, event);
400                 if (contentInOption_.HasDurationChanged()) {
401                     duration = contentInOption_.GetDuration();
402                 }
403                 delay = contentInOption_.GetDelay();
404                 isCustomOption_ = true;
405                 sharedInOption_ = contentInOption_;
406             }
407         }
408     }
409     if (controller_) {
410         controller_->SetDuration(duration);
411         if (context && context->GetIsDeclarative() && delay >= 0) {
412             controller_->SetStartDelay(delay);
413         }
414     }
415     if (contentTransition_) {
416         contentTransition_->SetTransition(contentInOption_, contentOutOption_);
417         contentTransition_->SetSharedTransition(sharedInOption_, sharedOutOption_);
418     }
419     if (frontDecorationTransition_ && !isSetOutOption) {
420         // do not need option in.
421         TweenOption optionIn;
422         frontDecorationTransition_->SetTransition(optionIn, tweenOption->GetTransitionFrontDecorationOption());
423         frontDecorationTransition_->SetSharedTransition(
424             optionIn, tweenOption->GetSharedTransitionFrontDecorationOption());
425     }
426     if (backgroundTransition_) {
427         backgroundTransition_->SetTransition(
428             tweenOption->GetTransitionBackgroundInOption(), tweenOption->GetTransitionBackgroundOutOption());
429     }
430 }
431 
BuildCombinedChild(const RefPtr<StackComponent> & component)432 void PageTransitionElement::BuildCombinedChild(const RefPtr<StackComponent>& component)
433 {
434     auto pageTransitionComponent = AceType::DynamicCast<PageTransitionComponent>(component);
435     if (!pageTransitionComponent) {
436         LOGE("Get PageTransitionComponent failed!");
437         return;
438     }
439     // create transition for content
440     auto box = AceType::MakeRefPtr<BoxComponent>();
441     Component::MergeRSNode(box);
442     auto front = AceType::MakeRefPtr<Decoration>();
443     front->SetBackgroundColor(Color::FromRGBO(0, 0, 0, 0.0));
444     box->SetFrontDecoration(front);
445     box->SetChild(pageTransitionComponent->GetContent());
446     auto transition = AceType::MakeRefPtr<TransitionComponent>(
447         TransitionComponent::AllocTransitionComponentId(), "frontDecoration_transition", box);
448 
449     auto clip = AceType::MakeRefPtr<ClipComponent>(transition);
450     Component::MergeRSNode(clip);
451     auto contentTransitionComponent = AceType::MakeRefPtr<TransitionComponent>(
452         TransitionComponent::AllocTransitionComponentId(), "page_transition_content", clip);
453 
454     // add transition for content
455     pageTransitionComponent->AppendChild(contentTransitionComponent);
456     StackElement::PerformBuild();
457 
458     if (children_.size() != 1) {
459         LOGE("the children size is error.");
460         return;
461     }
462     auto childIter = children_.begin();
463     auto child = *childIter;
464 
465     // child for content.
466     contentTransition_ = AceType::DynamicCast<TransitionElement>(child);
467     auto frontElement = contentTransition_->GetContentElement();
468     if (frontElement) {
469         frontDecorationTransition_ = DynamicCast<TransitionElement>(frontElement->GetFirstChild());
470     }
471 }
472 
BuildSeparatedChild(const RefPtr<StackComponent> & component)473 void PageTransitionElement::BuildSeparatedChild(const RefPtr<StackComponent>& component)
474 {
475     auto pageTransitionComponent = AceType::DynamicCast<PageTransitionComponent>(component);
476     if (!pageTransitionComponent) {
477         LOGE("BuildSeparatedChild : get PageTransitionComponent failed!");
478         return;
479     }
480     // add transition for background
481     pageTransitionComponent->AppendChild(
482         AceType::MakeRefPtr<TransitionComponent>(TransitionComponent::AllocTransitionComponentId(),
483             "page_transition_background", pageTransitionComponent->GetBackground()));
484 
485     // create transition for content
486     auto contentTransitionComponent =
487         AceType::MakeRefPtr<TransitionComponent>(TransitionComponent::AllocTransitionComponentId(),
488             "page_transition_content", pageTransitionComponent->GetContent());
489 
490     // add transition for content
491     pageTransitionComponent->AppendChild(contentTransitionComponent);
492     StackElement::PerformBuild();
493 
494     if (children_.size() != CHILDREN_SIZE_WHEN_SPLIT) {
495         LOGE("the children size is error.");
496         return;
497     }
498     auto childIter = children_.begin();
499     auto child = *childIter;
500 
501     // child for background
502     backgroundTransition_ = AceType::DynamicCast<TransitionElement>(child);
503     child = *(++childIter);
504 
505     // child for content.
506     contentTransition_ = AceType::DynamicCast<TransitionElement>(child);
507 }
508 
GetContentTransitionElement() const509 const RefPtr<TransitionElement>& PageTransitionElement::GetContentTransitionElement() const
510 {
511     return contentTransition_;
512 }
513 
GetBackgroundTransitionElement() const514 const RefPtr<TransitionElement>& PageTransitionElement::GetBackgroundTransitionElement() const
515 {
516     return backgroundTransition_;
517 }
518 
ProcessPageTransition(const RefPtr<PageTransition> & pageTransition,TransitionEvent event)519 TweenOption PageTransitionElement::ProcessPageTransition(
520     const RefPtr<PageTransition>& pageTransition, TransitionEvent event)
521 {
522     auto tweenOption = pageTransition->GetTweenOption();
523     // 1.SlideEffect
524     auto transitionDeclarativeTweenOption = TransitionDeclarativeTweenOption(isRightToLeft_, GetContext());
525     transitionDeclarativeTweenOption.CreateSlideEffectAnimation(
526         tweenOption, pageTransition->GetSlideEffect(), pageTransition->GetType(), direction_);
527     // 2. callback
528     auto onExitHandler = pageTransition->GetOnExitHandler();
529     auto onEnterHandler = pageTransition->GetOnEnterHandler();
530     RouteType type = RouteType::PUSH;
531     if (event == TransitionEvent::POP_START || event == TransitionEvent::POP_END) {
532         type = RouteType::POP;
533     }
534     if (onExitHandler || onEnterHandler) {
535         floatAnimation_ = AceType::MakeRefPtr<CurveAnimation<float>>(0.0f, 1.0f, pageTransition->GetCurve());
536         if (onExitHandler) {
537             floatAnimation_->AddListener(
538                 [type, onExitHandler](const float& progress) { onExitHandler(type, progress); });
539         }
540         if (onEnterHandler) {
541             floatAnimation_->AddListener(
542                 [type, onEnterHandler](const float& progress) { onEnterHandler(type, progress); });
543         }
544     }
545     // 3. delay curve
546     return tweenOption;
547 }
548 
GetCurrentPageTransition(TransitionEvent event,TransitionDirection direction) const549 RefPtr<PageTransition> PageTransitionElement::GetCurrentPageTransition(
550     TransitionEvent event, TransitionDirection direction) const
551 {
552     if (pageTransitions_.empty()) {
553         return nullptr;
554     }
555     auto type = GetPageTransitionType(event, direction);
556     auto pos = pageTransitions_.find(type);
557     if (pos != pageTransitions_.end()) {
558         return pos->second;
559     }
560 
561     if (direction == TransitionDirection::TRANSITION_IN) {
562         type = PageTransitionType::ENTER;
563     } else {
564         type = PageTransitionType::EXIT;
565     }
566     pos = pageTransitions_.find(type);
567     if (pos != pageTransitions_.end()) {
568         return pos->second;
569     }
570     return nullptr;
571 }
572 
GetPageTransitionType(TransitionEvent event,TransitionDirection direction)573 PageTransitionType PageTransitionElement::GetPageTransitionType(TransitionEvent event, TransitionDirection direction)
574 {
575     if (direction == TransitionDirection::TRANSITION_IN) {
576         if (event == TransitionEvent::POP_START) {
577             return PageTransitionType::ENTER_POP;
578         } else {
579             return PageTransitionType::ENTER_PUSH;
580         }
581     } else {
582         if (event == TransitionEvent::POP_START) {
583             return PageTransitionType::EXIT_POP;
584         } else {
585             return PageTransitionType::EXIT_PUSH;
586         }
587     }
588 }
589 
590 } // namespace OHOS::Ace
591