1 /*
2  * Copyright (c) 2022-2023 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_ng/manager/shared_overlay/shared_transition_effect.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components/common/layout/constants.h"
20 #include "core/components/common/properties/animation_option.h"
21 #include "core/components/common/properties/motion_path_evaluator.h"
22 #include "core/components_ng/base/frame_node.h"
23 #include "core/components_ng/layout/layout_property.h"
24 #include "core/components_ng/pattern/stage/page_pattern.h"
25 #include "core/components_ng/property/property.h"
26 
27 namespace OHOS::Ace::NG {
SharedTransitionEffect(const ShareId & shareId,const std::shared_ptr<SharedTransitionOption> & sharedOption)28 SharedTransitionEffect::SharedTransitionEffect(
29     const ShareId& shareId, const std::shared_ptr<SharedTransitionOption>& sharedOption)
30     : shareId_(shareId), option_(sharedOption)
31 {
32     std::string animatorName = "SharedTransition(" + shareId + ")";
33     controller_ = CREATE_ANIMATOR(animatorName.c_str());
34 }
35 
GetSharedTransitionEffect(const ShareId & shareId,const std::shared_ptr<SharedTransitionOption> & option)36 RefPtr<SharedTransitionEffect> SharedTransitionEffect::GetSharedTransitionEffect(
37     const ShareId& shareId, const std::shared_ptr<SharedTransitionOption>& option)
38 {
39     CHECK_NULL_RETURN(option, nullptr);
40     RefPtr<SharedTransitionEffect> effect;
41     if (option->type == SharedTransitionEffectType::SHARED_EFFECT_EXCHANGE) {
42         effect = AceType::MakeRefPtr<SharedTransitionExchange>(shareId, option);
43     } else {
44         effect = AceType::MakeRefPtr<SharedTransitionStatic>(shareId, option);
45     }
46     return effect;
47 }
48 
PerformFinishCallback()49 void SharedTransitionEffect::PerformFinishCallback()
50 {
51     for (const auto& callback : finishCallbacks_) {
52         if (callback) {
53             callback();
54         }
55     }
56     finishCallbacks_.clear();
57 }
58 
CreateOpacityAnimation(float startOpacity,float endOpacity,float finalOpacity,const WeakPtr<FrameNode> & node)59 bool SharedTransitionEffect::CreateOpacityAnimation(
60     float startOpacity, float endOpacity, float finalOpacity, const WeakPtr<FrameNode>& node)
61 {
62     if (NearEqual(startOpacity, endOpacity)) {
63         // no need to perform opacity animation
64         return true;
65     }
66     auto opacityAnimation = AceType::MakeRefPtr<CurveAnimation<float>>(startOpacity, endOpacity, option_->curve);
67     auto opacityListener = [weakFrame = node](const float& opacity) {
68         auto node = weakFrame.Upgrade();
69         CHECK_NULL_VOID(node);
70         node->GetRenderContext()->UpdateOpacity(opacity);
71     };
72     opacityAnimation->AddListener(opacityListener);
73     controller_->AddInterpolator(opacityAnimation);
74     controller_->AddStopListener([weakFrame = node, finalOpacity]() {
75         auto node = weakFrame.Upgrade();
76         CHECK_NULL_VOID(node);
77         node->GetRenderContext()->UpdateOpacity(finalOpacity);
78     });
79     return true;
80 }
81 
ApplyAnimation()82 bool SharedTransitionEffect::ApplyAnimation()
83 {
84     CHECK_NULL_RETURN(option_, false);
85     controller_->SetDuration(option_->duration);
86     controller_->SetStartDelay(option_->delay);
87     return true;
88 }
89 
SharedTransitionExchange(const ShareId & shareId,const std::shared_ptr<SharedTransitionOption> & sharedOption)90 SharedTransitionExchange::SharedTransitionExchange(
91     const ShareId& shareId, const std::shared_ptr<SharedTransitionOption>& sharedOption)
92     : SharedTransitionEffect(shareId, sharedOption)
93 {}
94 
Allow() const95 bool SharedTransitionExchange::Allow() const
96 {
97     auto dest = dest_.Upgrade();
98     auto src = src_.Upgrade();
99     if (!dest || !src) {
100         return false;
101     }
102     return !GetShareId().empty();
103 }
104 
CreateAnimation()105 bool SharedTransitionExchange::CreateAnimation()
106 {
107     auto src = src_.Upgrade();
108     auto dest = dest_.Upgrade();
109     if (!dest || !src) {
110         return false;
111     }
112     CHECK_NULL_RETURN(option_, false);
113     CHECK_NULL_RETURN(option_->curve, false);
114     if (!CreateTranslateAnimation(src, dest)) {
115         return false;
116     }
117     if (!CreateSizeAnimation(src, dest)) {
118         return false;
119     }
120     if (!CreateOpacityAnimation(src, dest)) {
121         return false;
122     }
123     return true;
124 }
125 
CreateTranslateAnimation(const RefPtr<FrameNode> & src,const RefPtr<FrameNode> & dest)126 bool SharedTransitionExchange::CreateTranslateAnimation(const RefPtr<FrameNode>& src, const RefPtr<FrameNode>& dest)
127 {
128     auto destOffset = dest->GetPaintRectOffsetToPage();
129     auto srcOffset = src->GetPaintRectOffsetToPage();
130     TAG_LOGD(AceLogTag::ACE_ANIMATION,
131         "Translate animation get Offset, share id: %{public}s. src: %{public}s, dest: %{public}s", GetShareId().c_str(),
132         srcOffset.ToString().c_str(), destOffset.ToString().c_str());
133     if (NearEqual(destOffset, srcOffset)) {
134         return true;
135     }
136     Offset diff { destOffset.GetX() - srcOffset.GetX(), destOffset.GetY() - srcOffset.GetY() };
137     auto translateAnimation = AceType::MakeRefPtr<CurveAnimation<DimensionOffset>>(Offset(0, 0), diff, option_->curve);
138     auto srcRenderContext = src->GetRenderContext();
139     std::optional<Vector5F> srcRotate;
140     if (option_->motionPathOption.IsValid()) {
141         auto motionPathEvaluator =
142             AceType::MakeRefPtr<MotionPathEvaluator>(option_->motionPathOption, Offset(0, 0), diff);
143         translateAnimation->SetEvaluator(motionPathEvaluator->CreateDimensionOffsetEvaluator());
144         if (option_->motionPathOption.GetRotate()) {
145             // Just need to add a rotation animation, the specific rotation Angle is calculated through the path
146             auto rotateAnimation = AceType::MakeRefPtr<CurveAnimation<float>>(0.0f, 0.0f, option_->curve);
147             rotateAnimation->SetEvaluator(motionPathEvaluator->CreateRotateEvaluator());
148             auto rotateListener = [weakSrc = WeakPtr<RenderContext>(srcRenderContext)](float value) {
149                 auto srcNode = weakSrc.Upgrade();
150                 CHECK_NULL_VOID(srcNode);
151                 // Rotate around the Z axis
152                 srcNode->UpdateTransformRotate({ 0, 0, 1, value, 0 });
153             };
154             rotateAnimation->AddListener(rotateListener);
155             controller_->AddInterpolator(rotateAnimation);
156             srcRotate = srcRenderContext->GetTransformRotateValue({ 0, 0, 1, 0, 0 });
157         }
158     }
159     auto translateListener = [weakSrc = WeakPtr<RenderContext>(srcRenderContext)](const DimensionOffset& value) {
160         auto srcNode = weakSrc.Upgrade();
161         CHECK_NULL_VOID(srcNode);
162         srcNode->SetSharedTranslate(static_cast<float>(value.GetX().Value()), static_cast<float>(value.GetY().Value()));
163     };
164     translateAnimation->AddListener(translateListener);
165     controller_->AddInterpolator(translateAnimation);
166     finishCallbacks_.emplace_back([weakSrc = WeakPtr<RenderContext>(srcRenderContext), srcRotate]() {
167         auto srcNode = weakSrc.Upgrade();
168         CHECK_NULL_VOID(srcNode);
169         srcNode->ResetSharedTranslate();
170         if (srcRotate) {
171             srcNode->UpdateTransformRotate(srcRotate.value());
172         }
173     });
174     return true;
175 }
176 
CreateSizeAnimation(const RefPtr<FrameNode> & src,const RefPtr<FrameNode> & dest)177 bool SharedTransitionExchange::CreateSizeAnimation(const RefPtr<FrameNode>& src, const RefPtr<FrameNode>& dest)
178 {
179     auto destSize = dest->GetRenderContext()->GetPaintRectWithoutTransform().GetSize();
180     auto srcSize = src->GetRenderContext()->GetPaintRectWithoutTransform().GetSize();
181     if (!destSize.IsPositive()) {
182         TAG_LOGW(AceLogTag::ACE_ANIMATION,
183             "DestSize is %{public}s, means we don't get the size correctly, so create sharedTransition failed"
184             ", sharedId:%{public}s",
185             destSize.ToString().c_str(), GetShareId().c_str());
186         return false;
187     }
188     TAG_LOGD(AceLogTag::ACE_ANIMATION,
189         "Size animation get size,  share id: %{public}s. src: %{public}s, dest: %{public}s", GetShareId().c_str(),
190         srcSize.ToString().c_str(), destSize.ToString().c_str());
191     if (NearEqual(srcSize, destSize)) {
192         return true;
193     }
194     const auto& magicProperty = src->GetLayoutProperty()->GetMagicItemProperty();
195     auto initAspectRatio = magicProperty.GetAspectRatio();
196     auto initSize = src->GetLayoutProperty()->GetCalcLayoutConstraint()
197                         ? src->GetLayoutProperty()->GetCalcLayoutConstraint()->selfIdealSize
198                         : std::nullopt;
199     auto sizeAnimation = AceType::MakeRefPtr<CurveAnimation<SizeF>>(srcSize, destSize, option_->curve);
200     auto sizeListener = [weakFrame = WeakPtr<FrameNode>(src), setAspect = initAspectRatio.has_value()](
201                             const SizeF& size) {
202         auto src = weakFrame.Upgrade();
203         CHECK_NULL_VOID(src);
204         src->GetLayoutProperty()->UpdateUserDefinedIdealSize(
205             CalcSize(CalcLength(size.Width()), CalcLength(size.Height())));
206         if (setAspect) {
207             src->GetLayoutProperty()->UpdateAspectRatio(size.Width() / size.Height());
208         }
209         // When call listener callback, the passenger has mounted to overlay, only need to measure passenger
210         // for better performance. Notice that the parent has been changed, layoutConstraint is not correct if we
211         // don't measure from parent, otherwise the result may be wrong if passenger has aspectRatio.
212         src->GetGeometryNode()->ResetParentLayoutConstraint();
213         src->GetLayoutProperty()->CleanDirty();
214         src->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
215     };
216     sizeAnimation->AddListener(sizeListener);
217     controller_->AddInterpolator(sizeAnimation);
218     finishCallbacks_.emplace_back([weakFrame = WeakPtr<FrameNode>(src), initSize, initAspectRatio]() {
219         auto src = weakFrame.Upgrade();
220         CHECK_NULL_VOID(src);
221         if (src->GetLayoutProperty()->GetCalcLayoutConstraint()) {
222             src->GetLayoutProperty()->GetCalcLayoutConstraint()->selfIdealSize = initSize;
223         }
224         if (initAspectRatio.has_value()) {
225             src->GetLayoutProperty()->UpdateAspectRatio(initAspectRatio.value());
226         }
227         src->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
228     });
229     return true;
230 }
231 
CreateOpacityAnimation(const RefPtr<FrameNode> & src,const RefPtr<FrameNode> & dest)232 bool SharedTransitionExchange::CreateOpacityAnimation(const RefPtr<FrameNode>& src, const RefPtr<FrameNode>& dest)
233 {
234     auto startOpacity = static_cast<float>(src->GetRenderContext()->GetOpacityValue(1.0));
235     auto endOpacity = static_cast<float>(dest->GetRenderContext()->GetOpacityValue(1.0));
236     return SharedTransitionEffect::CreateOpacityAnimation(startOpacity, endOpacity, startOpacity, src_);
237 }
238 
SetVisibleToDest(VisibleType type)239 bool SharedTransitionExchange::SetVisibleToDest(VisibleType type)
240 {
241     auto dest = dest_.Upgrade();
242     CHECK_NULL_RETURN(dest, false);
243     dest->GetLayoutProperty()->UpdateVisibility(type);
244     return true;
245 }
246 
DestRequestDefaultFocus()247 void SharedTransitionExchange::DestRequestDefaultFocus()
248 {
249     if (destVisible_ != VisibleType::VISIBLE) {
250         return;
251     }
252     auto dest = dest_.Upgrade();
253     CHECK_NULL_VOID(dest);
254     auto page = dest->GetPageNode();
255     CHECK_NULL_VOID(page);
256     auto pagePattern = page->GetPattern<PagePattern>();
257     CHECK_NULL_VOID(pagePattern);
258     pagePattern->FocusViewShow();
259 }
260 
SharedTransitionStatic(const ShareId & shareId,const std::shared_ptr<SharedTransitionOption> & sharedOption)261 SharedTransitionStatic::SharedTransitionStatic(
262     const ShareId& shareId, const std::shared_ptr<SharedTransitionOption>& sharedOption)
263     : SharedTransitionEffect(shareId, sharedOption)
264 {}
265 
Allow() const266 bool SharedTransitionStatic::Allow() const
267 {
268     auto shared = GetPassengerNode().Upgrade();
269     CHECK_NULL_RETURN(shared, false);
270     return !(GetShareId().empty());
271 }
272 
CreateAnimation()273 bool SharedTransitionStatic::CreateAnimation()
274 {
275     auto node = GetPassengerNode().Upgrade();
276     if (!node) {
277         return false;
278     }
279     // static transition only need opacity animation
280     auto initialOpacity = static_cast<float>(node->GetRenderContext()->GetOpacityValue(1.0));
281     if (dest_ == node) {
282         // anchor appearing, passenger is dest_, opacity 0 to initial opacity
283         return SharedTransitionEffect::CreateOpacityAnimation(0.0f, initialOpacity, initialOpacity, dest_);
284     }
285     // anchor disappearing, passenger is src_, opacity initial opacity to 0
286     return SharedTransitionEffect::CreateOpacityAnimation(initialOpacity, 0.0f, initialOpacity, src_);
287 }
288 
GetPassengerNode() const289 const WeakPtr<FrameNode>& SharedTransitionStatic::GetPassengerNode() const
290 {
291     return src_.Invalid() ? dest_ : src_;
292 }
293 
294 } // namespace OHOS::Ace::NG
295