• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 
16 #include "core/components_ng/pattern/refresh/refresh_pattern.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/memory/ace_type.h"
21 #include "base/utils/utils.h"
22 #include "core/animation/spring_curve.h"
23 #include "core/common/container.h"
24 #include "core/components/common/properties/animation_option.h"
25 #include "core/components/refresh/refresh_theme.h"
26 #include "core/components_ng/base/frame_node.h"
27 #include "core/components_ng/event/event_hub.h"
28 #include "core/components_ng/pattern/loading_progress/loading_progress_layout_property.h"
29 #include "core/components_ng/pattern/loading_progress/loading_progress_paint_property.h"
30 #include "core/components_ng/pattern/refresh/refresh_animation_state.h"
31 #include "core/components_ng/pattern/refresh/refresh_layout_property.h"
32 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
33 #include "core/components_ng/property/property.h"
34 #include "core/components_ng/render/animation_utils.h"
35 #include "core/pipeline/base/element_register.h"
36 #include "core/pipeline_ng/pipeline_context.h"
37 #include "frameworks/base/i18n/localization.h"
38 #include "frameworks/base/utils/time_util.h"
39 #include "frameworks/base/utils/utils.h"
40 #include "frameworks/core/components/common/layout/constants.h"
41 #include "frameworks/core/components_ng/pattern/loading_progress/loading_progress_pattern.h"
42 #include "frameworks/core/components_ng/pattern/text/text_pattern.h"
43 
44 namespace OHOS::Ace::NG {
45 
46 namespace {
47 constexpr float PERCENT = 0.01f; // Percent
48 constexpr float FOLLOW_TO_RECYCLE_DURATION = 600.0f;
49 constexpr float CUSTOM_BUILDER_ANIMATION_DURATION = 100.0f;
50 constexpr float LOADING_ANIMATION_DURATION = 350.0f;
51 constexpr float MAX_OFFSET = 100000.0f;
52 constexpr float HALF = 0.5f;
53 constexpr float BASE_SCALE = 0.707f; // std::sqrt(2)/2
54 constexpr Dimension TRIGGER_LOADING_DISTANCE = 16.0_vp;
55 constexpr Dimension TRIGGER_REFRESH_DISTANCE = 64.0_vp;
56 constexpr Dimension MAX_SCROLL_DISTANCE = 128.0_vp;
57 constexpr Dimension LOADING_PROGRESS_SIZE = 32.0_vp;
58 constexpr float DEFAULT_FRICTION = 62.0f;
59 const RefPtr<Curve> DEFAULT_CURVE = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
60 const std::string REFRESH_DRAG_SCENE = "refresh_drag_scene";
61 } // namespace
62 
OnAttachToFrameNode()63 void RefreshPattern::OnAttachToFrameNode()
64 {
65     auto host = GetHost();
66     CHECK_NULL_VOID(host);
67     host->GetRenderContext()->SetClipToBounds(true);
68     host->GetRenderContext()->UpdateClipEdge(true);
69 }
70 
OnModifyDone()71 void RefreshPattern::OnModifyDone()
72 {
73     Pattern::OnModifyDone();
74     auto host = GetHost();
75     CHECK_NULL_VOID(host);
76     auto hub = host->GetEventHub<EventHub>();
77     CHECK_NULL_VOID(hub);
78     auto gestureHub = hub->GetOrCreateGestureEventHub();
79     CHECK_NULL_VOID(gestureHub);
80     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
81     CHECK_NULL_VOID(layoutProperty);
82     InitPanEvent(gestureHub);
83     InitOnKeyEvent();
84     InitChildNode();
85     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
86         InitOffsetProperty();
87     } else {
88         triggerLoadingDistance_ = static_cast<float>(
89             std::clamp(layoutProperty->GetIndicatorOffset().value_or(TRIGGER_LOADING_DISTANCE).ConvertToPx(),
90                 -1.0f * TRIGGER_LOADING_DISTANCE.ConvertToPx(), TRIGGER_REFRESH_DISTANCE.ConvertToPx()));
91         InitLowVersionOffset();
92     }
93     RefreshStatusChangeEffect();
94     SetAccessibilityAction();
95 }
96 
CreateLayoutAlgorithm()97 RefPtr<LayoutAlgorithm> RefreshPattern::CreateLayoutAlgorithm()
98 {
99     auto refreshLayoutAlgorithm = MakeRefPtr<RefreshLayoutAlgorithm>();
100     if (isCustomBuilderExist_) {
101         refreshLayoutAlgorithm->SetCustomBuilderIndex(0);
102         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
103             refreshLayoutAlgorithm->SetBuilderMeasureBaseHeight(builderMeasureBaseHeight_);
104         } else {
105             refreshLayoutAlgorithm->SetCustomBuilderOffset(customBuilderOffset_);
106             refreshLayoutAlgorithm->SetScrollOffset(scrollOffset_);
107         }
108     }
109     return refreshLayoutAlgorithm;
110 }
111 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)112 void RefreshPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
113 {
114     if (panEvent_) {
115         return;
116     }
117     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
118         auto pattern = weak.Upgrade();
119         CHECK_NULL_VOID(pattern);
120         pattern->HandleDragStart(true, static_cast<float>(info.GetMainVelocity()));
121     };
122     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
123         auto pattern = weak.Upgrade();
124         CHECK_NULL_VOID(pattern);
125         pattern->HandleDragUpdate(static_cast<float>(info.GetMainDelta()), static_cast<float>(info.GetMainVelocity()));
126     };
127     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
128         auto pattern = weak.Upgrade();
129         CHECK_NULL_VOID(pattern);
130         pattern->HandleDragEnd(info.GetMainVelocity());
131     };
132     auto actionCancelTask = [weak = WeakClaim(this)]() {
133         auto pattern = weak.Upgrade();
134         CHECK_NULL_VOID(pattern);
135         pattern->HandleDragCancel();
136     };
137     PanDirection panDirection;
138     panDirection.type = PanDirection::VERTICAL;
139     if (panEvent_) {
140         gestureHub->RemovePanEvent(panEvent_);
141     }
142 
143     panEvent_ = MakeRefPtr<PanEvent>(
144         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
145     gestureHub->AddPanEvent(panEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
146 }
147 
InitOnKeyEvent()148 void RefreshPattern::InitOnKeyEvent()
149 {
150     if (isKeyEventRegisted_) {
151         return;
152     }
153     auto host = GetHost();
154     CHECK_NULL_VOID(host);
155     auto focusHub = host->GetFocusHub();
156     CHECK_NULL_VOID(focusHub);
157     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
158         auto pattern = wp.Upgrade();
159         CHECK_NULL_RETURN(pattern, false);
160         return pattern->OnKeyEvent(event);
161     };
162     isKeyEventRegisted_ = true;
163     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
164 }
165 
InitProgressNode()166 void RefreshPattern::InitProgressNode()
167 {
168     auto host = GetHost();
169     CHECK_NULL_VOID(host);
170     progressChild_ = FrameNode::CreateFrameNode(V2::LOADING_PROGRESS_ETS_TAG,
171         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LoadingProgressPattern>());
172     CHECK_NULL_VOID(progressChild_);
173     host->AddChild(progressChild_, -1);
174     auto gestureHub = progressChild_->GetEventHub<EventHub>();
175     if (gestureHub) {
176         gestureHub->SetEnabled(false);
177     }
178     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
179     CHECK_NULL_VOID(progressLayoutProperty);
180     progressLayoutProperty->UpdateUserDefinedIdealSize(
181         CalcSize(CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx()), CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx())));
182     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
183     CHECK_NULL_VOID(layoutProperty);
184     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
185     CHECK_NULL_VOID(progressPaintProperty);
186     progressPaintProperty->UpdateLoadingProgressOwner(LoadingProgressOwner::REFRESH);
187     if (layoutProperty->HasProgressColor()) {
188         progressPaintProperty->UpdateColor(layoutProperty->GetProgressColorValue());
189     }
190     progressChild_->MarkDirtyNode();
191 }
192 // the child need to add to be added to the first position in customBuilder mode,
193 // the child need to add to be added to the last position in loadingProgress mode.
InitChildNode()194 void RefreshPattern::InitChildNode()
195 {
196     auto host = GetHost();
197     CHECK_NULL_VOID(host);
198     if (isCustomBuilderExist_) {
199         if (progressChild_) {
200             host->RemoveChild(progressChild_);
201             progressChild_ = nullptr;
202         }
203     } else if (!progressChild_) {
204         InitProgressNode();
205         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
206             auto progressContext = progressChild_->GetRenderContext();
207             CHECK_NULL_VOID(progressContext);
208             progressContext->UpdateOpacity(0.0);
209         } else {
210             UpdateLoadingProgress();
211         }
212     }
213 }
214 
RefreshStatusChangeEffect()215 void RefreshPattern::RefreshStatusChangeEffect()
216 {
217     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
218     CHECK_NULL_VOID(layoutProperty);
219     auto refreshingProp = layoutProperty->GetIsRefreshing().value_or(false);
220     if (isRefreshing_ != refreshingProp) {
221         if (refreshingProp) {
222             QuickStartFresh();
223         } else {
224             QuickEndFresh();
225         }
226     }
227 }
228 
QuickStartFresh()229 void RefreshPattern::QuickStartFresh()
230 {
231     UpdateRefreshStatus(RefreshStatus::REFRESH);
232     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
233         QuickFirstChildAppear();
234         return;
235     }
236 
237     if (isCustomBuilderExist_) {
238         CustomBuilderRefreshingAnimation(false);
239     } else {
240         LoadingProgressRefreshingAnimation(false);
241     }
242 }
243 
QuickEndFresh()244 void RefreshPattern::QuickEndFresh()
245 {
246     SwitchToFinish();
247     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
248         QuickFirstChildDisappear();
249         return;
250     }
251 
252     if (isCustomBuilderExist_) {
253         CustomBuilderExit();
254     } else {
255         LoadingProgressExit();
256     }
257 }
258 
OnKeyEvent(const KeyEvent & event)259 bool RefreshPattern::OnKeyEvent(const KeyEvent& event)
260 {
261     if (event.code == KeyCode::KEY_F5 || (event.IsCombinationKey() && event.IsCtrlWith(KeyCode::KEY_R))) {
262         if (!isRefreshing_) {
263             QuickStartFresh();
264         }
265         return true;
266     }
267     return false;
268 }
269 
HandleDragStart(bool isDrag,float mainSpeed)270 void RefreshPattern::HandleDragStart(bool isDrag, float mainSpeed)
271 {
272     UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, mainSpeed, SceneStatus::START);
273     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
274         isSourceFromAnimation_ = !isDrag;
275         ResetAnimation();
276     } else {
277         HandleDragStartLowVersion();
278     }
279     // AccessibilityEventType::SCROLL_START
280 }
281 
HandleDragUpdate(float delta,float mainSpeed)282 void RefreshPattern::HandleDragUpdate(float delta, float mainSpeed)
283 {
284     UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, mainSpeed, SceneStatus::RUNNING);
285     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
286         // If dragging does not expand the refresh, there is no need to continue executing the code
287         if (NearZero(scrollOffset_) && NonPositive(delta)) {
288             return;
289         }
290         scrollOffset_ = std::clamp(scrollOffset_ + delta * CalculateFriction(), 0.0f, MAX_OFFSET);
291         if (!isSourceFromAnimation_) {
292             if (isRefreshing_) {
293                 UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
294             } else {
295                 UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, GetFollowRatio());
296                 if (LessNotEqual(scrollOffset_, static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()))) {
297                     UpdateRefreshStatus(RefreshStatus::DRAG);
298                 } else {
299                     UpdateRefreshStatus(RefreshStatus::OVER_DRAG);
300                 }
301             }
302         }
303         UpdateFirstChildPlacement();
304     } else {
305         HandleDragUpdateLowVersion(delta);
306     }
307 }
308 
HandleDragEnd(float speed)309 void RefreshPattern::HandleDragEnd(float speed)
310 {
311     UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, speed, SceneStatus::END);
312     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
313         SpeedTriggerAnimation(speed);
314     } else {
315         HandleDragEndLowVersion();
316     }
317 }
318 
HandleDragCancel()319 void RefreshPattern::HandleDragCancel()
320 {
321     HandleDragEnd(0.0f);
322 }
323 
CalculateFriction()324 float RefreshPattern::CalculateFriction()
325 {
326     auto host = GetHost();
327     CHECK_NULL_RETURN(host, 1.0f);
328     auto geometryNode = host->GetGeometryNode();
329     CHECK_NULL_RETURN(geometryNode, 1.0f);
330     auto contentHeight = geometryNode->GetPaddingSize().Height();
331     return NearZero(contentHeight) ? 1.0f : ScrollablePattern::CalculateFriction(scrollOffset_ / contentHeight);
332 }
333 
GetFollowRatio()334 float RefreshPattern::GetFollowRatio()
335 {
336     auto loadingVisibleHeight = GetLoadingVisibleHeight();
337     auto ratio = 0.0f;
338     if (!NearEqual(static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()), loadingVisibleHeight)) {
339         ratio = static_cast<float>(
340             (scrollOffset_ - loadingVisibleHeight) / (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - loadingVisibleHeight));
341     }
342     return std::clamp(ratio, 0.0f, 1.0f);
343 }
344 
FireStateChange(int32_t value)345 void RefreshPattern::FireStateChange(int32_t value)
346 {
347     auto refreshEventHub = GetEventHub<RefreshEventHub>();
348     CHECK_NULL_VOID(refreshEventHub);
349     refreshEventHub->FireOnStateChange(value);
350     if (refreshStatus_ == RefreshStatus::REFRESH && Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
351         auto host = GetHost();
352         CHECK_NULL_VOID(host);
353         auto inspectorId = host->GetInspectorId().value_or("");
354         Recorder::EventParamsBuilder builder;
355         builder.SetId(inspectorId)
356             .SetType(host->GetTag())
357             .SetEventType(Recorder::EventType::REFRESH)
358             .SetDescription(host->GetAutoEventParamValue(""));
359         Recorder::EventRecorder::Get().OnEvent(std::move(builder));
360     }
361 }
362 
FireRefreshing()363 void RefreshPattern::FireRefreshing()
364 {
365     auto refreshEventHub = GetEventHub<RefreshEventHub>();
366     CHECK_NULL_VOID(refreshEventHub);
367     refreshEventHub->FireOnRefreshing();
368 }
369 
FireChangeEvent(const std::string & value)370 void RefreshPattern::FireChangeEvent(const std::string& value)
371 {
372     auto refreshEventHub = GetEventHub<RefreshEventHub>();
373     CHECK_NULL_VOID(refreshEventHub);
374     refreshEventHub->FireChangeEvent(value);
375 }
376 
AddCustomBuilderNode(const RefPtr<NG::UINode> & builder)377 void RefreshPattern::AddCustomBuilderNode(const RefPtr<NG::UINode>& builder)
378 {
379     if (!builder) {
380         isCustomBuilderExist_ = false;
381         customBuilder_ = nullptr;
382         return;
383     }
384     auto host = GetHost();
385     CHECK_NULL_VOID(host);
386 
387     if (!isCustomBuilderExist_) {
388         host->AddChild(builder, 0);
389     } else {
390         auto customNodeChild = host->GetFirstChild();
391         CHECK_NULL_VOID(customNodeChild);
392         host->ReplaceChild(customNodeChild, builder);
393     }
394     customBuilder_ = AceType::DynamicCast<FrameNode>(builder);
395     isCustomBuilderExist_ = true;
396 }
397 
SetAccessibilityAction()398 void RefreshPattern::SetAccessibilityAction()
399 {
400     auto host = GetHost();
401     CHECK_NULL_VOID(host);
402     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
403     CHECK_NULL_VOID(accessibilityProperty);
404     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
405         const auto& pattern = weakPtr.Upgrade();
406         CHECK_NULL_VOID(pattern);
407         if (pattern->IsRefreshing()) {
408             return;
409         }
410         pattern->HandleDragStart(true, 0.0f);
411         for (float delta = 0.0f; LessNotEqual(delta, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
412              delta += TRIGGER_LOADING_DISTANCE.ConvertToPx()) {
413             pattern->HandleDragUpdate(delta, 0.0f);
414         }
415         pattern->HandleDragEnd(0.0f);
416     });
417 }
418 
InitCoordinationEvent(RefPtr<ScrollableCoordinationEvent> & coordinationEvent)419 void RefreshPattern::InitCoordinationEvent(RefPtr<ScrollableCoordinationEvent>& coordinationEvent)
420 {
421     auto onScrollEvent = [weak = WeakClaim(this)](float offset, float mainSpeed) -> bool {
422         auto pattern = weak.Upgrade();
423         CHECK_NULL_RETURN(pattern, false);
424         pattern->HandleDragUpdate(offset, mainSpeed);
425         return Positive(pattern->scrollOffset_) || NonNegative(offset);
426     };
427     coordinationEvent->SetOnScrollEvent(onScrollEvent);
428     auto onScrollStartEvent = [weak = WeakClaim(this)](bool isDrag, float mainSpeed) {
429         auto pattern = weak.Upgrade();
430         CHECK_NULL_VOID(pattern);
431         pattern->HandleDragStart(isDrag, mainSpeed);
432     };
433     coordinationEvent->SetOnScrollStartEvent(onScrollStartEvent);
434     auto onScrollEndEvent = [weak = WeakClaim(this)](float speed) {
435         auto pattern = weak.Upgrade();
436         CHECK_NULL_VOID(pattern);
437         pattern->HandleDragEnd(speed);
438     };
439     coordinationEvent->SetOnScrollEndEvent(onScrollEndEvent);
440 }
441 
UpdateRefreshStatus(RefreshStatus newStatus)442 void RefreshPattern::UpdateRefreshStatus(RefreshStatus newStatus)
443 {
444     if (refreshStatus_ == newStatus) {
445         return;
446     }
447     refreshStatus_ = newStatus;
448     if (refreshStatus_ == RefreshStatus::REFRESH) {
449         isRefreshing_ = true;
450         // the two-way binding of 'refreshing' variable need to be changed before 'onRefreshing' function is triggered
451         FireChangeEvent("true");
452         FireRefreshing();
453     } else {
454         isRefreshing_ = false;
455         FireChangeEvent("false");
456     }
457     FireStateChange(static_cast<int>(refreshStatus_));
458     TAG_LOGD(AceLogTag::ACE_REFRESH, "refresh status changed %{public}d", static_cast<int32_t>(refreshStatus_));
459 }
460 
SwitchToFinish()461 void RefreshPattern::SwitchToFinish()
462 {
463     if (refreshStatus_ != RefreshStatus::REFRESH && refreshStatus_ != RefreshStatus::DONE) {
464         UpdateRefreshStatus(RefreshStatus::INACTIVE);
465     } else {
466         UpdateRefreshStatus(RefreshStatus::DONE);
467     }
468 }
469 
UpdateLoadingProgressStatus(RefreshAnimationState state,float ratio)470 void RefreshPattern::UpdateLoadingProgressStatus(RefreshAnimationState state, float ratio)
471 {
472     // Need to check loadingProgress mode
473     CHECK_NULL_VOID(progressChild_);
474     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
475     CHECK_NULL_VOID(progressPaintProperty);
476     progressPaintProperty->UpdateRefreshAnimationState(state);
477     switch (state) {
478         case RefreshAnimationState::FOLLOW_HAND:
479         case RefreshAnimationState::RECYCLE:
480             progressPaintProperty->UpdateRefreshSizeScaleRatio(ratio);
481             break;
482         default:
483             break;
484     }
485     if (CheckNeedRender(progressPaintProperty->GetPropertyChangeFlag())) {
486         progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
487     }
488 }
489 
InitOffsetProperty()490 void RefreshPattern::InitOffsetProperty()
491 {
492     if (!offsetProperty_) {
493         auto propertyCallback = [weak = AceType::WeakClaim(this)](float scrollOffset) {
494             auto pattern = weak.Upgrade();
495             CHECK_NULL_VOID(pattern);
496             pattern->scrollOffset_ = scrollOffset;
497             pattern->UpdateFirstChildPlacement();
498         };
499         offsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
500         auto host = GetHost();
501         CHECK_NULL_VOID(host);
502         auto renderContext = host->GetRenderContext();
503         CHECK_NULL_VOID(renderContext);
504         renderContext->AttachNodeAnimatableProperty(offsetProperty_);
505         offsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
506     }
507 }
508 
UpdateFirstChildPlacement()509 void RefreshPattern::UpdateFirstChildPlacement()
510 {
511     auto host = GetHost();
512     CHECK_NULL_VOID(host);
513     auto geometryNode = host->GetGeometryNode();
514     CHECK_NULL_VOID(geometryNode);
515     auto refreshHeight = geometryNode->GetFrameSize().Height();
516     auto scrollOffset = std::clamp(scrollOffset_, 0.0f, refreshHeight);
517     if (progressChild_) {
518         if (isSourceFromAnimation_) {
519             UpdateLoadingProgressTranslate(0.0f);
520             UpdateScrollTransition(scrollOffset);
521         } else {
522             UpdateLoadingProgressTranslate(scrollOffset);
523             UpdateScrollTransition(scrollOffset);
524             UpdateLoadingProgressStatus(GetLoadingProgressStatus(), GetFollowRatio());
525         }
526     } else {
527         UpdateBuilderHeight(scrollOffset);
528     }
529 }
530 
UpdateScrollTransition(float scrollOffset)531 void RefreshPattern::UpdateScrollTransition(float scrollOffset)
532 {
533     auto host = GetHost();
534     CHECK_NULL_VOID(host);
535     // If the refresh has no children without loadingProgress, it does not need to be offset.
536     if (host->TotalChildCount() <= 1) {
537         return;
538     }
539     // Need to search for frameNode and skip ComponentNode
540     auto childNode = host->GetFirstChild();
541     while (!AceType::InstanceOf<FrameNode>(childNode) && !childNode->GetChildren().empty()) {
542         childNode = childNode->GetFirstChild();
543     }
544     auto scrollableNode = AceType::DynamicCast<FrameNode>(childNode);
545     CHECK_NULL_VOID(scrollableNode);
546     auto scrollableRenderContext = scrollableNode->GetRenderContext();
547     CHECK_NULL_VOID(scrollableRenderContext);
548     scrollableRenderContext->UpdateTransformTranslate({ 0.0f, scrollOffset, 0.0f });
549 }
550 
UpdateBuilderHeight(float builderHeight)551 void RefreshPattern::UpdateBuilderHeight(float builderHeight)
552 {
553     auto host = GetHost();
554     CHECK_NULL_VOID(host);
555     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
556     CHECK_NULL_VOID(layoutProperty);
557     builderMeasureBaseHeight_ = builderHeight;
558     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
559 }
560 
UpdateLoadingProgressTranslate(float scrollOffset)561 void RefreshPattern::UpdateLoadingProgressTranslate(float scrollOffset)
562 {
563     CHECK_NULL_VOID(progressChild_);
564     auto renderContext = progressChild_->GetRenderContext();
565     CHECK_NULL_VOID(renderContext);
566     auto loadingVisibleHeight = GetLoadingVisibleHeight();
567     if (GreatOrEqual(scrollOffset, loadingVisibleHeight) &&
568         !NearEqual(loadingVisibleHeight, static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()))) {
569         auto ratio = static_cast<float>(
570             (scrollOffset - loadingVisibleHeight) / (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - loadingVisibleHeight));
571         renderContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
572         renderContext->UpdateTransformTranslate({ 0.0f, (scrollOffset - loadingVisibleHeight) * HALF, 0.0f });
573     } else {
574         renderContext->UpdateOpacity(0.0f);
575     }
576 }
577 
GetLoadingVisibleHeight()578 float RefreshPattern::GetLoadingVisibleHeight()
579 {
580     CHECK_NULL_RETURN(progressChild_, 0.0f);
581     auto renderContext = progressChild_->GetRenderContext();
582     CHECK_NULL_RETURN(renderContext, 0.0f);
583     auto geometryNode = progressChild_->GetGeometryNode();
584     CHECK_NULL_RETURN(geometryNode, 0.0f);
585     auto loadingHeight = geometryNode->GetFrameSize().Height();
586     return (HALF + BASE_SCALE * HALF) * loadingHeight;
587 }
588 
SpeedTriggerAnimation(float speed)589 void RefreshPattern::SpeedTriggerAnimation(float speed)
590 {
591     auto targetOffset = (isSourceFromAnimation_ || LessNotEqual(scrollOffset_, TRIGGER_REFRESH_DISTANCE.ConvertToPx()))
592                             ? 0.0f
593                             : TRIGGER_REFRESH_DISTANCE.ConvertToPx();
594     auto dealSpeed = 0.0f;
595     if (!NearEqual(scrollOffset_, targetOffset)) {
596         dealSpeed = speed / (targetOffset - scrollOffset_);
597     }
598     if (!isSourceFromAnimation_ && refreshStatus_ == RefreshStatus::OVER_DRAG) {
599         UpdateRefreshStatus(RefreshStatus::REFRESH);
600         UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_TO_RECYCLE, GetFollowRatio());
601     } else if (NearZero(targetOffset)) {
602         SwitchToFinish();
603     }
604     ResetAnimation();
605     AnimationOption option;
606     auto curve = AceType::MakeRefPtr<InterpolatingSpring>(dealSpeed, 1.0f, 228.0f, 30.0f);
607     option.SetCurve(curve);
608     animation_ = AnimationUtils::StartAnimation(
609         option,
610         [&, weak = AceType::WeakClaim(this)]() {
611             auto pattern = weak.Upgrade();
612             CHECK_NULL_VOID(pattern);
613             pattern->offsetProperty_->Set(targetOffset);
614         },
615         [weak = AceType::WeakClaim(this)]() {
616             auto pattern = weak.Upgrade();
617             CHECK_NULL_VOID(pattern);
618             pattern->SpeedAnimationFinish();
619         });
620 }
621 
GetTargetOffset()622 float RefreshPattern::GetTargetOffset()
623 {
624     if (isSourceFromAnimation_) {
625         return 0.0f;
626     }
627     auto targetOffset = 0.0f;
628     switch (refreshStatus_) {
629         case RefreshStatus::OVER_DRAG:
630         case RefreshStatus::REFRESH:
631             targetOffset = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
632             break;
633         default:
634             targetOffset = 0.0f;
635             break;
636     }
637     return targetOffset;
638 }
639 
SpeedAnimationFinish()640 void RefreshPattern::SpeedAnimationFinish()
641 {
642     if (isRefreshing_) {
643         UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
644     } else {
645         UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, GetFollowRatio());
646     }
647 }
648 
QuickFirstChildAppear()649 void RefreshPattern::QuickFirstChildAppear()
650 {
651     UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
652     ResetAnimation();
653     AnimationOption option;
654     option.SetCurve(DEFAULT_CURVE);
655     option.SetDuration(LOADING_ANIMATION_DURATION);
656     animation_ = AnimationUtils::StartAnimation(
657         option, [&]() { offsetProperty_->Set(static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx())); });
658 }
659 
QuickFirstChildDisappear()660 void RefreshPattern::QuickFirstChildDisappear()
661 {
662     ResetAnimation();
663     AnimationOption option;
664     option.SetCurve(DEFAULT_CURVE);
665     option.SetDuration(LOADING_ANIMATION_DURATION);
666     animation_ = AnimationUtils::StartAnimation(
667         option, [&]() { offsetProperty_->Set(0.0f); },
668         [weak = AceType::WeakClaim(this)]() {
669             auto pattern = weak.Upgrade();
670             CHECK_NULL_VOID(pattern);
671             pattern->SpeedAnimationFinish();
672         });
673 }
674 
GetLoadingProgressStatus()675 RefreshAnimationState RefreshPattern::GetLoadingProgressStatus()
676 {
677     auto defaultValue = RefreshAnimationState::FOLLOW_HAND;
678     CHECK_NULL_RETURN(progressChild_, defaultValue);
679     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
680     CHECK_NULL_RETURN(progressPaintProperty, defaultValue);
681     return progressPaintProperty->GetRefreshAnimationState().value_or(defaultValue);
682 }
683 
ResetAnimation()684 void RefreshPattern::ResetAnimation()
685 {
686     float currentOffset = scrollOffset_;
687     AnimationUtils::StopAnimation(animation_);
688     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
689         offsetProperty_->Set(currentOffset);
690     } else {
691         lowVersionOffset_->Set(currentOffset);
692     }
693 }
694 
UpdateDragFRCSceneInfo(const std::string & scene,float speed,SceneStatus sceneStatus)695 void RefreshPattern::UpdateDragFRCSceneInfo(const std::string& scene, float speed, SceneStatus sceneStatus)
696 {
697     auto host = GetHost();
698     CHECK_NULL_VOID(host);
699     host->AddFRCSceneInfo(scene, std::abs(speed), sceneStatus);
700 }
701 
InitLowVersionOffset()702 void RefreshPattern::InitLowVersionOffset()
703 {
704     if (!lowVersionOffset_) {
705         auto propertyCallback = [weak = AceType::WeakClaim(this)](float scrollOffset) {
706             auto pattern = weak.Upgrade();
707             CHECK_NULL_VOID(pattern);
708             pattern->scrollOffset_ = scrollOffset;
709             pattern->UpdateChild();
710         };
711         lowVersionOffset_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
712         auto host = GetHost();
713         CHECK_NULL_VOID(host);
714         auto renderContext = host->GetRenderContext();
715         CHECK_NULL_VOID(renderContext);
716         renderContext->AttachNodeAnimatableProperty(lowVersionOffset_);
717     }
718 }
719 
UpdateChild()720 void RefreshPattern::UpdateChild()
721 {
722     if (customBuilder_) {
723         UpdateCustomBuilderProperty();
724     } else {
725         UpdateLoadingProgress();
726     }
727 }
728 
HandleDragStartLowVersion()729 void RefreshPattern::HandleDragStartLowVersion()
730 {
731     if (isRefreshing_) {
732         return;
733     }
734     scrollOffset_ = 0.0f;
735     UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, 0.0f);
736 }
737 
HandleDragUpdateLowVersion(float delta)738 void RefreshPattern::HandleDragUpdateLowVersion(float delta)
739 {
740     if (isRefreshing_) {
741         return;
742     }
743     scrollOffset_ = GetScrollOffset(delta);
744     if (LessNotEqual(scrollOffset_, static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()))) {
745         UpdateRefreshStatus(RefreshStatus::DRAG);
746     } else {
747         UpdateRefreshStatus(RefreshStatus::OVER_DRAG);
748     }
749     if (customBuilder_) {
750         HandleCustomBuilderDragUpdateStage();
751         return;
752     }
753     UpdateLoadingProgress();
754     if (GreatNotEqual(scrollOffset_, triggerLoadingDistance_)) {
755         auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
756         CHECK_NULL_VOID(progressPaintProperty);
757         float triggerRefreshDistance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
758         float ratio =
759             NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
760                 ? 1.0f
761                 : (scrollOffset_ - triggerLoadingDistance_) / (triggerRefreshDistance - triggerLoadingDistance_);
762         progressPaintProperty->UpdateRefreshSizeScaleRatio(std::clamp(ratio, 0.0f, 1.0f));
763     }
764 }
765 
HandleDragEndLowVersion()766 void RefreshPattern::HandleDragEndLowVersion()
767 {
768     if (isRefreshing_) {
769         return;
770     }
771     if (customBuilder_) {
772         HandleCustomBuilderDragEndStage();
773         return;
774     }
775     if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
776         UpdateRefreshStatus(RefreshStatus::REFRESH);
777         LoadingProgressRefreshingAnimation(true);
778     } else {
779         SwitchToFinish();
780         LoadingProgressExit();
781     }
782     // AccessibilityEventType::SCROLL_END
783 }
784 
LoadingProgressRefreshingAnimation(bool isDrag)785 void RefreshPattern::LoadingProgressRefreshingAnimation(bool isDrag)
786 {
787     UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, 1.0f);
788     ResetAnimation();
789     AnimationOption option;
790     if (isDrag) {
791         option.SetCurve(AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f));
792         option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
793     } else {
794         option.SetCurve(DEFAULT_CURVE);
795         option.SetDuration(LOADING_ANIMATION_DURATION);
796     }
797     animation_ = AnimationUtils::StartAnimation(
798         option, [&]() { lowVersionOffset_->Set(TRIGGER_REFRESH_DISTANCE.ConvertToPx()); });
799 }
800 
LoadingProgressExit()801 void RefreshPattern::LoadingProgressExit()
802 {
803     ResetAnimation();
804     AnimationOption option;
805     option.SetCurve(DEFAULT_CURVE);
806     option.SetDuration(LOADING_ANIMATION_DURATION);
807     animation_ = AnimationUtils::StartAnimation(
808         option, [&]() { lowVersionOffset_->Set(0.0f); },
809         [weak = AceType::WeakClaim(this)]() {
810             auto pattern = weak.Upgrade();
811             CHECK_NULL_VOID(pattern);
812             pattern->UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, 0.0f);
813         });
814 }
815 
UpdateLoadingProgress()816 void RefreshPattern::UpdateLoadingProgress()
817 {
818     float loadingProgressOffset =
819         std::clamp(scrollOffset_, triggerLoadingDistance_, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
820     UpdateLoadingMarginTop(loadingProgressOffset);
821     float triggerRefreshDistance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
822     float ratio = NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
823                       ? 1.0f
824                       : (loadingProgressOffset - triggerLoadingDistance_) /
825                             (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - triggerLoadingDistance_);
826     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
827     CHECK_NULL_VOID(progressPaintProperty);
828     progressPaintProperty->UpdateRefreshSizeScaleRatio(ratio);
829     auto progressContext = progressChild_->GetRenderContext();
830     CHECK_NULL_VOID(progressContext);
831     progressContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
832     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
833 }
834 
CustomBuilderRefreshingAnimation(bool isDrag)835 void RefreshPattern::CustomBuilderRefreshingAnimation(bool isDrag)
836 {
837     ResetAnimation();
838     AnimationOption option;
839     if (isDrag) {
840         option.SetCurve(AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f));
841         option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
842     } else {
843         option.SetCurve(DEFAULT_CURVE);
844         option.SetDuration(CUSTOM_BUILDER_ANIMATION_DURATION);
845     }
846     animation_ = AnimationUtils::StartAnimation(
847         option, [&]() { lowVersionOffset_->Set(TRIGGER_REFRESH_DISTANCE.ConvertToPx()); });
848 }
849 
CustomBuilderExit()850 void RefreshPattern::CustomBuilderExit()
851 {
852     ResetAnimation();
853     AnimationOption option;
854     option.SetDuration(CUSTOM_BUILDER_ANIMATION_DURATION);
855     option.SetCurve(DEFAULT_CURVE);
856     animation_ = AnimationUtils::StartAnimation(option, [&]() { lowVersionOffset_->Set(0.0f); });
857 }
858 
UpdateCustomBuilderProperty()859 void RefreshPattern::UpdateCustomBuilderProperty()
860 {
861     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetFrameSize();
862     auto maxScroll = static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx());
863     customBuilderOffset_ = std::clamp(scrollOffset_, triggerLoadingDistance_, maxScroll - customBuilderSize.Height());
864     float triggerRefreshDistance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
865     float ratio = NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
866                       ? 1.0f
867                       : (customBuilderOffset_ - triggerLoadingDistance_) /
868                             (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - triggerLoadingDistance_);
869     auto customBuilderContext = customBuilder_->GetRenderContext();
870     CHECK_NULL_VOID(customBuilderContext);
871     customBuilderContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
872     auto host = GetHost();
873     CHECK_NULL_VOID(host);
874     host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
875 }
876 
HandleCustomBuilderDragUpdateStage()877 void RefreshPattern::HandleCustomBuilderDragUpdateStage()
878 {
879     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetMarginFrameSize();
880     auto maxScroll = MAX_SCROLL_DISTANCE.ConvertToPx();
881     if (NearZero(static_cast<double>(customBuilder_->GetGeometryNode()->GetMarginFrameSize().Height()))) {
882         return;
883     }
884     if (LessNotEqual(static_cast<double>(maxScroll - customBuilderSize.Height()),
885             static_cast<double>(triggerLoadingDistance_))) {
886         return;
887     }
888     UpdateCustomBuilderProperty();
889 }
890 
HandleCustomBuilderDragEndStage()891 void RefreshPattern::HandleCustomBuilderDragEndStage()
892 {
893     if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
894         UpdateRefreshStatus(RefreshStatus::REFRESH);
895         CustomBuilderRefreshingAnimation(true);
896     } else {
897         SwitchToFinish();
898         CustomBuilderExit();
899     }
900 }
901 
UpdateLoadingMarginTop(float top)902 void RefreshPattern::UpdateLoadingMarginTop(float top)
903 {
904     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
905     CHECK_NULL_VOID(progressLayoutProperty);
906     MarginProperty marginProperty;
907     marginProperty.left = CalcLength(0.0f);
908     marginProperty.right = CalcLength(0.0f);
909     marginProperty.bottom = CalcLength(0.0f);
910     marginProperty.top = CalcLength(top);
911     progressLayoutProperty->UpdateMargin(marginProperty);
912 }
913 
GetScrollOffset(float delta)914 float RefreshPattern::GetScrollOffset(float delta)
915 {
916     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
917     CHECK_NULL_RETURN(layoutProperty, 0.0f);
918     auto frictionRatio = static_cast<float>(layoutProperty->GetFriction().value_or(DEFAULT_FRICTION)) * PERCENT;
919     auto scrollY = delta * frictionRatio;
920     return std::clamp(scrollOffset_ + scrollY, 0.0f, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
921 }
922 } // namespace OHOS::Ace::NG
923