1 /*
2  * Copyright (c) 2024 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 #include "core/components_ng/manager/drag_drop/utils/drag_animation_helper.h"
16 
17 #include "core/animation/animation_pub.h"
18 #include "core/components_ng/base/frame_node.h"
19 #include "core/components_ng/event/drag_event.h"
20 #include "core/components_ng/event/gesture_event_hub.h"
21 #include "core/components_ng/pattern/menu/menu_theme.h"
22 #include "core/components_ng/pattern/overlay/overlay_manager.h"
23 #include "core/components_ng/render/animation_utils.h"
24 #include "core/components_ng/render/render_context.h"
25 #include "core/components_v2/inspector/inspector_constants.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27 
28 namespace OHOS::Ace::NG {
29 namespace {
30 constexpr int32_t BEFORE_LIFTING_TIME = 650;
31 constexpr int32_t IMAGE_SHOW_TIME = 50;
32 constexpr int32_t PIXELMAP_ANIMATION_DURATION = 300;
33 constexpr int32_t BADGE_ANIMATION_DURATION = 200;
34 constexpr int32_t BADGE_ANIMATION_DELAY = 100;
35 constexpr float DEFAULT_ANIMATION_SCALE = 0.95f;
36 constexpr float GATHER_SPRING_RESPONSE = 0.304f;
37 constexpr float GATHER_SPRING_DAMPING_FRACTION = 0.97f;
38 constexpr float GRID_MOVE_SCALE = 0.2f;
39 constexpr float LIST_MOVE_SCALE = 0.2f;
40 constexpr float EULER_NUMBER = 2.71828f;
41 constexpr float GATHER_OFFSET_RADIUS = 0.1f;
42 constexpr float PIXELMAP_DRAG_SCALE_MULTIPLE = 1.05f;
43 constexpr float BADGE_ANIMATION_SCALE = 1.0f;
44 constexpr Dimension BADGE_RELATIVE_OFFSET = 8.0_vp;
45 constexpr Dimension BADGE_DEFAULT_SIZE = 24.0_vp;
46 constexpr Dimension BADGE_TEXT_FONT_SIZE = 14.0_fp;
47 const Color BADGE_TEXT_FONT_COLOR = Color::FromString("#ffffffff");
48 const Color BADGE_BACKGROUND_COLOR = Color::FromString("#ff007dff");
49 constexpr float DEFAULT_INTERPOLATING_SPRING_VELOCITY = 10.0f;
50 constexpr float DEFAULT_INTERPOLATING_SPRING_MASS = 1.0f;
51 constexpr float DEFAULT_INTERPOLATING_SPRING_STIFFNESS = 410.0f;
52 constexpr float DEFAULT_INTERPOLATING_SPRING_DAMPING = 38.0f;
53 }
54 
CalcDistanceBeforeLifting(bool isGrid,CalcResult & calcResult,OffsetF gatherNodeCenter,const std::vector<GatherNodeChildInfo> & gatherNodeChildrenInfo)55 void DragAnimationHelper::CalcDistanceBeforeLifting(bool isGrid, CalcResult& calcResult, OffsetF gatherNodeCenter,
56     const std::vector<GatherNodeChildInfo>& gatherNodeChildrenInfo)
57 {
58     for (const auto& child : gatherNodeChildrenInfo) {
59         auto imageNode = child.imageNode.Upgrade();
60         CHECK_NULL_VOID(imageNode);
61         auto width = child.width;
62         auto height = child.height;
63         OffsetF curPos = {child.offset.GetX() + child.halfWidth, child.offset.GetY() + child.halfHeight};
64         float dis = sqrt(pow(gatherNodeCenter.GetX() - curPos.GetX(), 2) +
65             pow(gatherNodeCenter.GetY() - curPos.GetY(), 2));
66         calcResult.maxDistance = std::max(calcResult.maxDistance, dis);
67         calcResult.minDistance = calcResult.minDistance < 0 ? dis : std::min(calcResult.minDistance, dis);
68         if (isGrid) {
69             calcResult.maxTranslation = calcResult.maxTranslation < 0 ? std::min(width, height) :
70                 std::min(calcResult.maxTranslation, std::min(width, height));
71         } else {
72             calcResult.maxTranslation = calcResult.maxTranslation < 0 ? height :
73                 std::min(calcResult.maxTranslation, height);
74         }
75     }
76     calcResult.maxTranslation *= isGrid ? GRID_MOVE_SCALE : LIST_MOVE_SCALE;
77 }
78 
CalcOffsetToTarget(OffsetF curPos,OffsetF targetPos,CalcResult & calcResult)79 OffsetF DragAnimationHelper::CalcOffsetToTarget(OffsetF curPos, OffsetF targetPos, CalcResult& calcResult)
80 {
81     if (NearZero(calcResult.maxDistance) || NearZero(calcResult.minDistance) || calcResult.maxTranslation < 0) {
82         return { 0.0f, 0.0f };
83     }
84 
85     float xDis = targetPos.GetX() - curPos.GetX();
86     float yDis = targetPos.GetY() - curPos.GetY();
87     float dis = sqrt(pow(xDis, 2) + pow(yDis, 2));
88     if (NearZero(dis)) {
89         return { 0.0f, 0.0f };
90     }
91     auto trans = calcResult.maxTranslation * pow(EULER_NUMBER, -(dis - calcResult.minDistance) /
92         calcResult.maxDistance * GATHER_OFFSET_RADIUS);
93     float x = xDis * trans / dis;
94     float y = yDis * trans / dis;
95     return { x, y };
96 }
97 
PlayGatherNodeTranslateAnimation(const RefPtr<DragEventActuator> & actuator,const RefPtr<OverlayManager> & overlayManager)98 void DragAnimationHelper::PlayGatherNodeTranslateAnimation(const RefPtr<DragEventActuator>& actuator,
99     const RefPtr<OverlayManager>& overlayManager)
100 {
101     CHECK_NULL_VOID(actuator);
102     AnimationOption option;
103     option.SetDuration(BEFORE_LIFTING_TIME);
104     option.SetCurve(Curves::SHARP);
105     auto frameNode = actuator->GetFrameNode();
106     auto gatherNodeCenter = frameNode->GetPaintRectCenter();
107     auto gatherNodeChildrenInfo = overlayManager->GetGatherNodeChildrenInfo();
108 
109     bool isGrid = frameNode->GetTag() == V2::GRID_ITEM_ETS_TAG;
110     CalcResult calcResult = { 0.0f, -1.0f, -1.0f };
111     CalcDistanceBeforeLifting(isGrid, calcResult, gatherNodeCenter, gatherNodeChildrenInfo);
112     AnimationUtils::Animate(
113         option,
114         [gatherNodeCenter, gatherNodeChildrenInfo, calcResult]() mutable {
115             for (const auto& child : gatherNodeChildrenInfo) {
116                 auto imageNode = child.imageNode.Upgrade();
117                 CHECK_NULL_VOID(imageNode);
118                 auto imageContext = imageNode->GetRenderContext();
119                 CHECK_NULL_VOID(imageContext);
120                 auto curPos = child.offset + OffsetF(child.halfWidth, child.halfHeight);
121                 auto offset = CalcOffsetToTarget(curPos, gatherNodeCenter, calcResult);
122                 imageContext->UpdatePosition(OffsetT<Dimension>(
123                     Dimension(child.offset.GetX() + offset.GetX()),
124                     Dimension(child.offset.GetY() + offset.GetY())));
125             }
126         });
127 }
128 
PlayGatherNodeOpacityAnimation(const RefPtr<OverlayManager> & overlayManager)129 void DragAnimationHelper::PlayGatherNodeOpacityAnimation(const RefPtr<OverlayManager>& overlayManager)
130 {
131     CHECK_NULL_VOID(overlayManager);
132     auto gatherNodeChildrenInfo = overlayManager->GetGatherNodeChildrenInfo();
133 
134     AnimationOption opacityOption;
135     opacityOption.SetDuration(IMAGE_SHOW_TIME);
136     opacityOption.SetCurve(AceType::MakeRefPtr<CubicCurve>(0.0f, 0.0f, 1.0f, 1.0f));
137 
138     for (const auto& child : gatherNodeChildrenInfo) {
139         auto imageNode = child.imageNode.Upgrade();
140         CHECK_NULL_VOID(imageNode);
141         auto imageContext = imageNode->GetRenderContext();
142         CHECK_NULL_VOID(imageContext);
143         imageContext->OpacityAnimation(opacityOption, 0.0f, 1.0f);
144     }
145 }
146 
PlayGatherAnimationBeforeLifting(const RefPtr<DragEventActuator> & actuator)147 void DragAnimationHelper::PlayGatherAnimationBeforeLifting(const RefPtr<DragEventActuator>& actuator)
148 {
149     TAG_LOGI(AceLogTag::ACE_DRAG, "Play gather animation before lifting");
150     CHECK_NULL_VOID(actuator);
151     if (!actuator->IsNeedGather()) {
152         return;
153     }
154     auto pipeline = PipelineContext::GetCurrentContext();
155     CHECK_NULL_VOID(pipeline);
156     auto manager = pipeline->GetOverlayManager();
157     CHECK_NULL_VOID(manager);
158     auto frameNode = actuator->GetFrameNode();
159     CHECK_NULL_VOID(frameNode);
160     auto gatherNode = actuator->GetGatherNode();
161     CHECK_NULL_VOID(gatherNode);
162     auto imageContext = gatherNode->GetRenderContext();
163     CHECK_NULL_VOID(imageContext);
164     imageContext->UpdatePosition(OffsetT<Dimension>(Dimension(0.0f), Dimension(0.0f)));
165     auto gatherNodeChildrenInfo = actuator->GetGatherNodeChildrenInfo();
166     DragEventActuator::MountGatherNode(manager, frameNode, gatherNode, gatherNodeChildrenInfo);
167     actuator->ClearGatherNodeChildrenInfo();
168     pipeline->FlushSyncGeometryNodeTasks();
169     manager->SetIsGatherWithMenu(false);
170     PlayGatherNodeOpacityAnimation(manager);
171     PlayGatherNodeTranslateAnimation(actuator, manager);
172 }
173 
PlayNodeAnimationBeforeLifting(const RefPtr<FrameNode> & frameNode)174 void DragAnimationHelper::PlayNodeAnimationBeforeLifting(const RefPtr<FrameNode>& frameNode)
175 {
176     CHECK_NULL_VOID(frameNode);
177     auto previewOptions = frameNode->GetDragPreviewOption();
178     if (!previewOptions.defaultAnimationBeforeLifting) {
179         return;
180     }
181     AnimationOption option;
182     option.SetDuration(BEFORE_LIFTING_TIME);
183     auto springCurve = AceType::MakeRefPtr<InterpolatingSpring>(DEFAULT_INTERPOLATING_SPRING_VELOCITY,
184         DEFAULT_INTERPOLATING_SPRING_MASS, DEFAULT_INTERPOLATING_SPRING_STIFFNESS,
185         DEFAULT_INTERPOLATING_SPRING_DAMPING);
186     option.SetCurve(springCurve);
187     auto renderContext = frameNode->GetRenderContext();
188     CHECK_NULL_VOID(renderContext);
189     renderContext->UpdateTransformScale({ 1.0f, 1.0f });
190 
191     AnimationUtils::Animate(
192         option,
193         [renderContext]() mutable {
194             CHECK_NULL_VOID(renderContext);
195             renderContext->UpdateTransformScale({ DEFAULT_ANIMATION_SCALE, DEFAULT_ANIMATION_SCALE });
196         });
197 }
198 
PlayNodeResetAnimation(const RefPtr<DragEventActuator> & actuator)199 void DragAnimationHelper::PlayNodeResetAnimation(const RefPtr<DragEventActuator>& actuator)
200 {
201     CHECK_NULL_VOID(actuator);
202     auto frameNode = actuator->GetFrameNode();
203     CHECK_NULL_VOID(frameNode);
204     bool defaultAnimationBeforeLifting = frameNode->GetDragPreviewOption().defaultAnimationBeforeLifting;
205     if (!defaultAnimationBeforeLifting) {
206         return;
207     }
208     auto frameContext = frameNode->GetRenderContext();
209     CHECK_NULL_VOID(frameContext);
210     auto layoutProperty = frameNode->GetLayoutProperty();
211     if (layoutProperty) {
212         layoutProperty->UpdateVisibility(VisibleType::VISIBLE);
213     }
214     AnimationOption option;
215     option.SetDuration(PIXELMAP_ANIMATION_DURATION);
216     const RefPtr<Curve> curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(0.33f, 0.67f, 1.0f);
217     option.SetCurve(curve);
218     AnimationUtils::Animate(
219         option,
220         [frameContext]() mutable {
221             frameContext->UpdateTransformScale({ 1.0f, 1.0f });
222         },
223         option.GetOnFinishEvent());
224 }
225 
PlayGatherAnimation(const RefPtr<FrameNode> & frameNode,const RefPtr<OverlayManager> & overlayManager)226 void DragAnimationHelper::PlayGatherAnimation(const RefPtr<FrameNode>& frameNode,
227     const RefPtr<OverlayManager>& overlayManager)
228 {
229     TAG_LOGI(AceLogTag::ACE_DRAG, "Play gather animation");
230     CHECK_NULL_VOID(frameNode);
231     auto gatherNodeCenter = frameNode->GetPaintRectCenter();
232     CHECK_NULL_VOID(overlayManager);
233     auto gatherNodeChildrenInfo = overlayManager->GetGatherNodeChildrenInfo();
234     BorderRadiusProperty borderRadius;
235     borderRadius.SetRadius(0.0_vp);
236     borderRadius.multiValued = false;
237     for (const auto& child : gatherNodeChildrenInfo) {
238         auto imageNode = child.imageNode.Upgrade();
239         CHECK_NULL_VOID(imageNode);
240         auto imageContext = imageNode->GetRenderContext();
241         CHECK_NULL_VOID(imageContext);
242         imageContext->UpdateBorderRadius(borderRadius);
243     }
244 
245     AnimationOption option;
246     option.SetDuration(PIXELMAP_ANIMATION_DURATION);
247     const RefPtr<Curve> curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(GATHER_SPRING_RESPONSE,
248         GATHER_SPRING_DAMPING_FRACTION, 0.0f);
249     option.SetCurve(curve);
250     auto geometryNode = frameNode->GetGeometryNode();
251     CHECK_NULL_VOID(geometryNode);
252     auto frameNodeSize = geometryNode->GetFrameSize();
253     auto renderContext = frameNode->GetRenderContext();
254     CHECK_NULL_VOID(renderContext);
255     GatherAnimationInfo gatherAnimationInfo = { PIXELMAP_DRAG_SCALE_MULTIPLE, frameNodeSize.Width(),
256         frameNodeSize.Height(), gatherNodeCenter, renderContext->GetBorderRadius() };
257     AnimationUtils::Animate(
258         option,
259         [overlayManager, gatherAnimationInfo]() {
260             DragDropManager::UpdateGatherNodeAttr(overlayManager, gatherAnimationInfo);
261         },
262         option.GetOnFinishEvent());
263 }
264 
ShowBadgeAnimation(const RefPtr<FrameNode> & textNode)265 void DragAnimationHelper::ShowBadgeAnimation(const RefPtr<FrameNode>& textNode)
266 {
267     auto pipelineContext = PipelineContext::GetCurrentContext();
268     CHECK_NULL_VOID(pipelineContext);
269     auto dragDropManager = pipelineContext->GetDragDropManager();
270     CHECK_NULL_VOID(dragDropManager);
271     if (!dragDropManager->IsShowBadgeAnimation()) {
272         return;
273     }
274     CHECK_NULL_VOID(textNode);
275     auto textNodeContext = textNode->GetRenderContext();
276     CHECK_NULL_VOID(textNodeContext);
277     textNodeContext->UpdateTransformScale({ 0.0f, 0.0f });
278     RefPtr<Curve> interpolatingSpring = AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 628.0f, 40.0f);
279     CHECK_NULL_VOID(interpolatingSpring);
280     AnimationOption textOption;
281     textOption.SetDuration(BADGE_ANIMATION_DURATION);
282     textOption.SetCurve(interpolatingSpring);
283     textOption.SetDelay(BADGE_ANIMATION_DELAY);
284     AnimationUtils::Animate(
285         textOption,
286         [textNodeContext]() mutable {
287             textNodeContext->UpdateTransformScale({ BADGE_ANIMATION_SCALE, BADGE_ANIMATION_SCALE });
288         },
289         textOption.GetOnFinishEvent());
290 
291     dragDropManager->SetIsShowBadgeAnimation(false);
292 }
293 
CalcBadgeTextOffset(const RefPtr<MenuPattern> & menuPattern,const RefPtr<FrameNode> & imageNode,const RefPtr<PipelineBase> & context,int32_t badgeLength)294 OffsetF DragAnimationHelper::CalcBadgeTextOffset(const RefPtr<MenuPattern>& menuPattern,
295     const RefPtr<FrameNode>& imageNode, const RefPtr<PipelineBase>& context, int32_t badgeLength)
296 {
297     CHECK_NULL_RETURN(imageNode, OffsetF());
298     CHECK_NULL_RETURN(menuPattern, OffsetF());
299     auto offset = imageNode->GetPaintRectOffset();
300     auto width = imageNode->GetGeometryNode()->GetFrameSize().Width();
301     auto scaleAfter = menuPattern->GetPreviewAfterAnimationScale();
302     auto menuTheme = context->GetTheme<NG::MenuTheme>();
303     CHECK_NULL_RETURN(menuTheme, OffsetF());
304     auto previewAfterAnimationScale =
305         LessNotEqual(scaleAfter, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : scaleAfter;
306     double textOffsetX = offset.GetX() + width * previewAfterAnimationScale -
307         BADGE_RELATIVE_OFFSET.ConvertToPx() - (BADGE_RELATIVE_OFFSET.ConvertToPx() * badgeLength);
308     double textOffsetY = offset.GetY() - BADGE_RELATIVE_OFFSET.ConvertToPx();
309     return OffsetF(textOffsetX, textOffsetY);
310 }
311 
CalcBadgeTextPosition(const RefPtr<MenuPattern> & menuPattern,const RefPtr<OverlayManager> & manager,const RefPtr<FrameNode> & imageNode,const RefPtr<FrameNode> & textNode)312 void DragAnimationHelper::CalcBadgeTextPosition(const RefPtr<MenuPattern>& menuPattern,
313     const RefPtr<OverlayManager>& manager, const RefPtr<FrameNode>& imageNode, const RefPtr<FrameNode>& textNode)
314 {
315     CHECK_NULL_VOID(manager);
316     CHECK_NULL_VOID(textNode);
317     CHECK_NULL_VOID(menuPattern);
318     auto pipelineContext = PipelineContext::GetCurrentContext();
319     CHECK_NULL_VOID(pipelineContext);
320     auto dragDropManager = pipelineContext->GetDragDropManager();
321     CHECK_NULL_VOID(dragDropManager);
322     auto frameNode = FrameNode::GetFrameNode(menuPattern->GetTargetTag(), menuPattern->GetTargetId());
323     CHECK_NULL_VOID(frameNode);
324     auto badgeNumber = frameNode->GetDragPreviewOption().GetCustomerBadgeNumber();
325     auto childSize = badgeNumber.has_value() ? static_cast<size_t>(badgeNumber.value()) :
326                                         manager->GetGatherNodeChildrenInfo().size() + 1;
327     TAG_LOGI(AceLogTag::ACE_DRAG, "Badge node number %{public}d, children count %{public}d",
328         badgeNumber.value_or(-1), static_cast<int32_t>(manager->GetGatherNodeChildrenInfo().size()));
329     auto badgeLength = std::to_string(childSize).size();
330     UpdateBadgeLayoutAndRenderContext(textNode, badgeLength, childSize);
331     auto textRenderContext = textNode->GetRenderContext();
332     CHECK_NULL_VOID(textRenderContext);
333     auto pipeline = PipelineBase::GetCurrentContext();
334     CHECK_NULL_VOID(pipeline);
335     auto offset = CalcBadgeTextOffset(menuPattern, imageNode, pipeline, badgeLength);
336     textRenderContext->UpdatePosition(OffsetT<Dimension>(Dimension(offset.GetX()), Dimension(offset.GetY())));
337     textNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE);
338     textNode->MarkModifyDone();
339     textNode->SetLayoutDirtyMarked(true);
340     textNode->SetActive(true);
341     auto context = textNode->GetContext();
342     if (context) {
343         context->FlushUITaskWithSingleDirtyNode(textNode);
344     }
345     pipeline->FlushSyncGeometryNodeTasks();
346 }
347 
UpdateBadgeLayoutAndRenderContext(const RefPtr<FrameNode> & textNode,int32_t badgeLength,int32_t childSize)348 void DragAnimationHelper::UpdateBadgeLayoutAndRenderContext(
349     const RefPtr<FrameNode>& textNode, int32_t badgeLength, int32_t childSize)
350 {
351     if (childSize <= 1) {
352         return;
353     }
354     CHECK_NULL_VOID(textNode);
355     auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
356     CHECK_NULL_VOID(textLayoutProperty);
357     textLayoutProperty->UpdateContent(std::to_string(childSize));
358     textLayoutProperty->UpdateMaxLines(1);
359     textLayoutProperty->UpdateFontWeight(FontWeight::MEDIUM);
360     textLayoutProperty->UpdateTextColor(BADGE_TEXT_FONT_COLOR);
361     textLayoutProperty->UpdateFontSize(BADGE_TEXT_FONT_SIZE);
362     textLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
363     int64_t textWidth = BADGE_DEFAULT_SIZE.ConvertToPx() + (BADGE_RELATIVE_OFFSET.ConvertToPx() * (badgeLength - 1));
364     auto textSize = CalcSize(NG::CalcLength(textWidth), NG::CalcLength(BADGE_DEFAULT_SIZE.ConvertToPx()));
365     textLayoutProperty->UpdateUserDefinedIdealSize(textSize);
366 
367     auto textRenderContext = textNode->GetRenderContext();
368     CHECK_NULL_VOID(textRenderContext);
369     textRenderContext->SetVisible(true);
370     textRenderContext->UpdateBackgroundColor(BADGE_BACKGROUND_COLOR);
371     BorderRadiusProperty borderRadius;
372     borderRadius.SetRadius(BADGE_DEFAULT_SIZE);
373     textRenderContext->UpdateBorderRadius(borderRadius);
374 }
375 
UpdateGatherNodeToTop()376 void DragAnimationHelper::UpdateGatherNodeToTop()
377 {
378     auto mainPipeline = PipelineContext::GetMainPipelineContext();
379     CHECK_NULL_VOID(mainPipeline);
380     auto manager = mainPipeline->GetOverlayManager();
381     CHECK_NULL_VOID(manager);
382     manager->UpdateGatherNodeToTop();
383 }
384 
ShowGatherAnimationWithMenu(const RefPtr<FrameNode> & menuWrapperNode)385 void DragAnimationHelper::ShowGatherAnimationWithMenu(const RefPtr<FrameNode>& menuWrapperNode)
386 {
387     TAG_LOGI(AceLogTag::ACE_DRAG, "Show gather animation with menu");
388     auto mainPipeline = PipelineContext::GetMainPipelineContext();
389     CHECK_NULL_VOID(mainPipeline);
390     auto manager = mainPipeline->GetOverlayManager();
391     CHECK_NULL_VOID(manager);
392     manager->SetIsGatherWithMenu(true);
393 
394     mainPipeline->AddAfterRenderTask([weakWrapperNode = AceType::WeakClaim(AceType::RawPtr(menuWrapperNode)),
395         weakManager = AceType::WeakClaim(AceType::RawPtr(manager))]() {
396         auto menuWrapperNode = weakWrapperNode.Upgrade();
397         CHECK_NULL_VOID(menuWrapperNode);
398         auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
399         CHECK_NULL_VOID(menuWrapperPattern);
400         auto manager = weakManager.Upgrade();
401         auto textNode = menuWrapperPattern->GetBadgeNode();
402         auto imageNode = menuWrapperPattern->GetPreview();
403         auto menuNode = menuWrapperPattern->GetMenu();
404         CHECK_NULL_VOID(menuNode);
405         auto menuPattern = menuNode->GetPattern<MenuPattern>();
406         DragAnimationHelper::PlayGatherAnimation(imageNode, manager);
407         DragAnimationHelper::CalcBadgeTextPosition(menuPattern, manager, imageNode, textNode);
408         DragAnimationHelper::ShowBadgeAnimation(textNode);
409     });
410 }
411 }