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