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