• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/pattern/loading_progress/loading_progress_layout_property.h"
28 #include "core/components_ng/pattern/loading_progress/loading_progress_paint_property.h"
29 #include "core/components_ng/pattern/refresh/refresh_layout_property.h"
30 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
31 #include "core/components_ng/property/property.h"
32 #include "core/components_ng/render/animation_utils.h"
33 #include "core/pipeline/base/element_register.h"
34 #include "core/pipeline_ng/pipeline_context.h"
35 #include "frameworks/base/i18n/localization.h"
36 #include "frameworks/base/utils/time_util.h"
37 #include "frameworks/base/utils/utils.h"
38 #include "frameworks/core/components/common/layout/constants.h"
39 #include "frameworks/core/components_ng/pattern/loading_progress/loading_progress_pattern.h"
40 #include "frameworks/core/components_ng/pattern/text/text_pattern.h"
41 
42 namespace OHOS::Ace::NG {
43 
44 namespace {
45 constexpr float PERCENT = 0.01; // Percent
46 constexpr float FOLLOW_TO_RECYCLE_DURATION = 600;
47 constexpr float LOADING_EXIT_DURATION = 350;
48 constexpr Dimension TRIGGER_LOADING_DISTANCE = 16.0_vp;
49 constexpr Dimension TRIGGER_REFRESH_DISTANCE = 64.0_vp;
50 constexpr Dimension MAX_SCROLL_DISTANCE = 128.0_vp;
51 constexpr Dimension LOADING_PROGRESS_SIZE = 32.0_vp;
52 constexpr float DEFAULT_FRICTION = 64.0f;
53 constexpr int32_t STATE_PROGRESS_LOADING = 1;
54 constexpr int32_t STATE_PROGRESS_RECYCLE = 2;
55 constexpr int32_t STATE_PROGRESS_DRAG = 3;
56 } // namespace
57 
OnModifyDone()58 void RefreshPattern::OnModifyDone()
59 {
60     auto host = GetHost();
61     CHECK_NULL_VOID(host);
62     auto hub = host->GetEventHub<EventHub>();
63     CHECK_NULL_VOID(hub);
64     auto gestureHub = hub->GetOrCreateGestureEventHub();
65     CHECK_NULL_VOID(gestureHub);
66     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
67     CHECK_NULL_VOID(layoutProperty);
68     triggerLoadingDistance_ = static_cast<float>(
69         std::clamp(layoutProperty->GetIndicatorOffset().value_or(TRIGGER_LOADING_DISTANCE).ConvertToPx(),
70             -1.0f * TRIGGER_LOADING_DISTANCE.ConvertToPx(), TRIGGER_REFRESH_DISTANCE.ConvertToPx()));
71     InitPanEvent(gestureHub);
72     CheckCoordinationEvent();
73     if (!progressChild_) {
74         progressChild_ = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(host->TotalChildCount() - 1));
75         LoadingProgressReset();
76     }
77 
78     auto paintProperty = GetPaintProperty<RefreshRenderProperty>();
79     CHECK_NULL_VOID(paintProperty);
80     auto refreshingProp = paintProperty->GetIsRefreshing().value_or(false);
81     if (isRefreshing_ != refreshingProp) {
82         if (refreshingProp) {
83             ReplaceLoadingProgressNode();
84             TriggerRefresh();
85             LoadingProgressAppear();
86         } else {
87             LoadingProgressExit();
88         }
89     }
90 }
91 
CheckCoordinationEvent()92 void RefreshPattern::CheckCoordinationEvent()
93 {
94     auto host = GetHost();
95     CHECK_NULL_VOID(host);
96     auto scrollableNode = FindScrollableChild();
97     scrollableNode_ = WeakClaim(AceType::RawPtr(scrollableNode));
98     CHECK_NULL_VOID(scrollableNode);
99     auto scrollablePattern = scrollableNode->GetPattern<ScrollablePattern>();
100     CHECK_NULL_VOID(scrollablePattern);
101     auto coordinationEvent = AceType::MakeRefPtr<ScrollableCoordinationEvent>();
102     auto onScrollEvent = [weak = WeakClaim(this)](double offset) {
103         auto pattern = weak.Upgrade();
104         CHECK_NULL_VOID(pattern);
105         pattern->HandleDragUpdate(static_cast<float>(offset));
106     };
107     coordinationEvent->SetOnScrollEvent(onScrollEvent);
108     auto onScrollStartEvent = [weak = WeakClaim(this)]() {
109         auto pattern = weak.Upgrade();
110         CHECK_NULL_VOID(pattern);
111         pattern->HandleDragStart();
112     };
113     coordinationEvent->SetOnScrollStartEvent(onScrollStartEvent);
114     auto onScrollEndEvent = [weak = WeakClaim(this)]() {
115         auto pattern = weak.Upgrade();
116         CHECK_NULL_VOID(pattern);
117         pattern->HandleDragEnd();
118     };
119     coordinationEvent->SetOnScrollEndEvent(onScrollEndEvent);
120     scrollablePattern->SetCoordinationEvent(coordinationEvent);
121 }
122 
FindScrollableChild()123 RefPtr<FrameNode> RefreshPattern::FindScrollableChild()
124 {
125     auto host = GetHost();
126     CHECK_NULL_RETURN(host, nullptr);
127     std::queue<RefPtr<FrameNode>> frameNodeQueue;
128     frameNodeQueue.push(host);
129     while (!frameNodeQueue.empty()) {
130         auto size = frameNodeQueue.size();
131         while (size > 0) {
132             auto node = frameNodeQueue.front();
133             CHECK_NULL_RETURN(node, nullptr);
134             if (AceType::InstanceOf<ScrollablePattern>(node->GetPattern())) {
135                 return node;
136             }
137             frameNodeQueue.pop();
138             auto children = node->GetChildren();
139             for (auto const& child : children) {
140                 auto childNode = DynamicCast<FrameNode>(child);
141                 if (childNode) {
142                     frameNodeQueue.push(childNode);
143                 }
144             }
145             size--;
146         }
147     }
148     return nullptr;
149 }
150 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> &,bool,bool)151 bool RefreshPattern::OnDirtyLayoutWrapperSwap(
152     const RefPtr<LayoutWrapper>& /*dirty*/, bool /*skipMeasure*/, bool /*skipLayout*/)
153 {
154     return false;
155 }
156 
TriggerRefresh()157 void RefreshPattern::TriggerRefresh()
158 {
159     isRefreshing_ = true;
160     FireChangeEvent("true");
161     FireRefreshing();
162     TriggerStatusChange(RefreshStatus::REFRESH);
163 }
164 
LoadingProgressRecycle()165 void RefreshPattern::LoadingProgressRecycle()
166 {
167     CHECK_NULL_VOID(progressChild_);
168     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
169     CHECK_NULL_VOID(progressPaintProperty);
170     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::RECYCLE));
171     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
172 }
173 
ReplaceLoadingProgressNode()174 void RefreshPattern::ReplaceLoadingProgressNode()
175 {
176     auto host = GetHost();
177     CHECK_NULL_VOID(host);
178     if (progressChild_) {
179         host->RemoveChild(progressChild_);
180     }
181     auto loadingProgressChild = FrameNode::CreateFrameNode(V2::LOADING_PROGRESS_ETS_TAG,
182         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LoadingProgressPattern>());
183     CHECK_NULL_VOID(loadingProgressChild);
184     host->AddChild(loadingProgressChild);
185     progressChild_ = loadingProgressChild;
186     host->RebuildRenderContextTree();
187     LoadingProgressReset();
188 }
189 
LoadingProgressReset()190 void RefreshPattern::LoadingProgressReset()
191 {
192     CHECK_NULL_VOID(progressChild_);
193     UpdateLoadingProgress(STATE_PROGRESS_LOADING, 0.0f);
194     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
195     CHECK_NULL_VOID(progressLayoutProperty);
196     progressLayoutProperty->UpdateUserDefinedIdealSize(
197         CalcSize(CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx()), CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx())));
198     ResetLoadingProgressColor();
199     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
200     CHECK_NULL_VOID(progressPaintProperty);
201     progressPaintProperty->UpdateLoadingProgressOwner(LoadingProgressOwner::REFRESH);
202     scrollOffset_.SetY(0.0f);
203     progressChild_->MarkDirtyNode();
204 }
205 
OnExitAnimationFinish()206 void RefreshPattern::OnExitAnimationFinish()
207 {
208     ReplaceLoadingProgressNode();
209     TriggerFinish();
210     CHECK_NULL_VOID(progressChild_);
211     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
212 }
213 
TriggerInActive()214 void RefreshPattern::TriggerInActive()
215 {
216     isRefreshing_ = false;
217     FireChangeEvent("false");
218     TriggerStatusChange(RefreshStatus::INACTIVE);
219 }
220 
TriggerDone()221 void RefreshPattern::TriggerDone()
222 {
223     isRefreshing_ = false;
224     FireChangeEvent("false");
225     TriggerStatusChange(RefreshStatus::DONE);
226 }
227 
TriggerFinish()228 void RefreshPattern::TriggerFinish()
229 {
230     if (refreshStatus_ == RefreshStatus::REFRESH) {
231         TriggerDone();
232     } else {
233         TriggerInActive();
234     }
235 }
236 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)237 void RefreshPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
238 {
239     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& /*info*/) {
240         auto pattern = weak.Upgrade();
241         CHECK_NULL_VOID(pattern);
242         pattern->HandleDragStart();
243     };
244     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
245         auto pattern = weak.Upgrade();
246         CHECK_NULL_VOID(pattern);
247         pattern->HandleDragUpdate(static_cast<float>(info.GetMainDelta()));
248     };
249     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& /*info*/) {
250         auto pattern = weak.Upgrade();
251         CHECK_NULL_VOID(pattern);
252         pattern->HandleDragEnd();
253     };
254     auto actionCancelTask = [weak = WeakClaim(this)]() {
255         auto pattern = weak.Upgrade();
256         CHECK_NULL_VOID(pattern);
257         pattern->HandleDragCancel();
258     };
259     PanDirection panDirection;
260     panDirection.type = PanDirection::VERTICAL;
261     if (panEvent_) {
262         gestureHub->RemovePanEvent(panEvent_);
263     }
264 
265     float distance = static_cast<float>(Dimension(DEFAULT_PAN_DISTANCE, DimensionUnit::VP).ConvertToPx());
266     panEvent_ = MakeRefPtr<PanEvent>(
267         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
268     gestureHub->AddPanEvent(panEvent_, panDirection, 1, distance);
269 }
270 
HandleDragStart()271 void RefreshPattern::HandleDragStart()
272 {
273     if (isRefreshing_) {
274         return;
275     }
276     TriggerStatusChange(RefreshStatus::DRAG);
277     CHECK_NULL_VOID(progressChild_);
278     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
279     CHECK_NULL_VOID(progressPaintProperty);
280     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::FOLLOW_HAND));
281     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
282 }
283 
HandleDragUpdate(float delta)284 void RefreshPattern::HandleDragUpdate(float delta)
285 {
286     if (NearZero(delta) || isRefreshing_) {
287         LOGI("Delta is near zero or isRefreshing!");
288         return;
289     }
290     CHECK_NULL_VOID(progressChild_);
291     scrollOffset_.SetY(GetScrollOffset(delta));
292     if (scrollOffset_.GetY() > triggerLoadingDistance_) {
293         auto refreshFollowRadio = GetFollowRatio();
294         UpdateLoadingProgress(STATE_PROGRESS_DRAG, refreshFollowRadio);
295         UpdateLoadingMarginTop(scrollOffset_.GetY());
296         auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
297         CHECK_NULL_VOID(progressPaintProperty);
298         progressPaintProperty->UpdateRefreshFollowRatio(refreshFollowRadio);
299     }
300 
301     if (scrollOffset_.GetY() > TRIGGER_REFRESH_DISTANCE.ConvertToPx()) {
302         TriggerStatusChange(RefreshStatus::OVER_DRAG);
303     }
304     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
305 }
306 
UpdateLoadingProgress(int32_t state,float ratio)307 void RefreshPattern::UpdateLoadingProgress(int32_t state, float ratio)
308 {
309     CHECK_NULL_VOID(progressChild_);
310     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
311     CHECK_NULL_VOID(progressLayoutProperty);
312     auto scale = std::clamp(ratio, 0.0f, 1.0f);
313     switch (state) {
314         case STATE_PROGRESS_LOADING:
315             scale = 0.0f;
316             UpdateLoadingMarginTop(triggerLoadingDistance_);
317             break;
318         case STATE_PROGRESS_RECYCLE:
319             scale = 1.0f;
320             UpdateLoadingMarginTop(TRIGGER_REFRESH_DISTANCE.ConvertToPx());
321             break;
322         default:;
323     }
324     auto progressContext = progressChild_->GetRenderContext();
325     CHECK_NULL_VOID_NOLOG(progressContext);
326     progressContext->UpdateOpacity(scale);
327 }
328 
GetFollowRatio()329 float RefreshPattern::GetFollowRatio()
330 {
331     auto triggerLoading = std::clamp(triggerLoadingDistance_, 0.0f,
332         static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()));
333     if (GreatNotEqual(TRIGGER_REFRESH_DISTANCE.ConvertToPx(), triggerLoading)) {
334         return (scrollOffset_.GetY() - triggerLoading) / (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - triggerLoading);
335     }
336     return 1.0f;
337 }
338 
GetFadeAwayRatio()339 float RefreshPattern::GetFadeAwayRatio()
340 {
341     CHECK_NULL_RETURN(progressChild_, 0.0f);
342     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
343     CHECK_NULL_RETURN(progressLayoutProperty, 0.0f);
344     auto& marginProperty = progressLayoutProperty->GetMarginProperty();
345     CHECK_NULL_RETURN(marginProperty, 0.0f);
346     auto triggerLoading = std::clamp(triggerLoadingDistance_, 0.0f,
347         static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()));
348     if (GreatNotEqual(TRIGGER_REFRESH_DISTANCE.ConvertToPx(), triggerLoading)) {
349         return (marginProperty->top->GetDimension().ConvertToPx() - triggerLoading) /
350                (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - triggerLoading);
351     }
352     return 0.0f;
353 }
354 
TransitionPeriodAnimation()355 void RefreshPattern::TransitionPeriodAnimation()
356 {
357     auto pipeline = PipelineContext::GetCurrentContext();
358     CHECK_NULL_VOID(pipeline);
359     CHECK_NULL_VOID(progressChild_);
360     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
361     CHECK_NULL_VOID(progressPaintProperty);
362 
363     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
364     CHECK_NULL_VOID(progressLayoutProperty);
365     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::FOLLOW_TO_RECYCLE));
366     progressPaintProperty->UpdateRefreshTransitionRatio(0.0f);
367     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
368     pipeline->FlushUITasks();
369 
370     auto curve = AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f);
371     AnimationOption option;
372     option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
373     option.SetCurve(curve);
374     option.SetIteration(1);
375 
376     AnimationUtils::OpenImplicitAnimation(option, curve, [weak = AceType::WeakClaim(this)]() {
377         auto pattern = weak.Upgrade();
378         CHECK_NULL_VOID(pattern);
379         pattern->LoadingProgressRecycle();
380     });
381     auto distance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
382     scrollOffset_.SetY(distance);
383     UpdateLoadingMarginTop(distance);
384     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
385     pipeline->FlushUITasks();
386     AnimationUtils::CloseImplicitAnimation();
387 }
388 
LoadingProgressAppear()389 void RefreshPattern::LoadingProgressAppear()
390 {
391     CHECK_NULL_VOID(progressChild_);
392     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
393     CHECK_NULL_VOID(progressPaintProperty);
394     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::RECYCLE));
395     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
396     auto pipeline = PipelineContext::GetCurrentContext();
397     CHECK_NULL_VOID(pipeline);
398     pipeline->FlushUITasks();
399 
400     AnimationOption option;
401     option.SetDuration(LOADING_EXIT_DURATION);
402     auto curve = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
403     AnimationUtils::OpenImplicitAnimation(option, curve, nullptr);
404     scrollOffset_.SetY(TRIGGER_REFRESH_DISTANCE.ConvertToPx());
405     UpdateLoadingProgress(STATE_PROGRESS_RECYCLE, 1.0f);
406     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
407     pipeline->FlushUITasks();
408     AnimationUtils::CloseImplicitAnimation();
409 }
410 
LoadingProgressExit()411 void RefreshPattern::LoadingProgressExit()
412 {
413     CHECK_NULL_VOID(progressChild_);
414     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
415     CHECK_NULL_VOID(progressPaintProperty);
416     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::FADEAWAY));
417     progressPaintProperty->UpdateRefreshFadeAwayRatio(GetFadeAwayRatio());
418     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
419     auto pipeline = PipelineContext::GetCurrentContext();
420     CHECK_NULL_VOID(pipeline);
421     pipeline->FlushUITasks();
422 
423     AnimationOption option;
424     option.SetDuration(LOADING_EXIT_DURATION);
425     auto curve = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
426     AnimationUtils::OpenImplicitAnimation(option, curve, [weak = AceType::WeakClaim(this)]() {
427         auto pattern = weak.Upgrade();
428         CHECK_NULL_VOID(pattern);
429         pattern->OnExitAnimationFinish();
430     });
431 
432     scrollOffset_.SetY(0.0f);
433     UpdateLoadingProgress(STATE_PROGRESS_LOADING, 0.0f);
434     progressPaintProperty->UpdateRefreshFadeAwayRatio(0.0f);
435     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
436     pipeline->FlushUITasks();
437     AnimationUtils::CloseImplicitAnimation();
438 }
439 
HandleDragEnd()440 void RefreshPattern::HandleDragEnd()
441 {
442     if (isRefreshing_) {
443         return;
444     }
445     auto triggerRefreshDistance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
446     if (scrollOffset_.GetY() >= triggerRefreshDistance) {
447         TriggerRefresh();
448         TransitionPeriodAnimation();
449         return;
450     }
451     LoadingProgressExit();
452 }
453 
TriggerStatusChange(RefreshStatus newStatus)454 void RefreshPattern::TriggerStatusChange(RefreshStatus newStatus)
455 {
456     if (refreshStatus_ == newStatus) {
457         return;
458     }
459     refreshStatus_ = newStatus;
460     FireStateChange(static_cast<int>(refreshStatus_));
461 }
462 
HandleDragCancel()463 void RefreshPattern::HandleDragCancel()
464 {
465     LoadingProgressExit();
466 }
467 
FireStateChange(int32_t value)468 void RefreshPattern::FireStateChange(int32_t value)
469 {
470     auto refreshEventHub = GetEventHub<RefreshEventHub>();
471     CHECK_NULL_VOID(refreshEventHub);
472     refreshEventHub->FireOnStateChange(value);
473 }
474 
FireRefreshing()475 void RefreshPattern::FireRefreshing()
476 {
477     auto refreshEventHub = GetEventHub<RefreshEventHub>();
478     CHECK_NULL_VOID(refreshEventHub);
479     refreshEventHub->FireOnRefreshing();
480 }
481 
FireChangeEvent(const std::string & value)482 void RefreshPattern::FireChangeEvent(const std::string& value)
483 {
484     auto refreshEventHub = GetEventHub<RefreshEventHub>();
485     CHECK_NULL_VOID(refreshEventHub);
486     refreshEventHub->FireChangeEvent(value);
487 }
488 
GetScrollOffset(float delta)489 float RefreshPattern::GetScrollOffset(float delta)
490 {
491     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
492     CHECK_NULL_RETURN(layoutProperty, 0.0f);
493     auto frictionRatio = static_cast<float>(layoutProperty->GetFriction().value_or(DEFAULT_FRICTION)) * PERCENT;
494     auto scrollY = delta * frictionRatio;
495     auto scrollOffset = std::clamp(scrollOffset_.GetY() + scrollY, static_cast<float>(0.0f),
496         static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
497     return scrollOffset;
498 }
499 
ResetLoadingProgressColor()500 void RefreshPattern::ResetLoadingProgressColor()
501 {
502     auto pipeline = PipelineContext::GetCurrentContext();
503     CHECK_NULL_VOID(pipeline);
504     auto themeManager = pipeline->GetThemeManager();
505     CHECK_NULL_VOID(themeManager);
506     auto theme = themeManager->GetTheme<RefreshTheme>();
507     CHECK_NULL_VOID(theme);
508     CHECK_NULL_VOID(progressChild_);
509     auto paintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
510     CHECK_NULL_VOID(paintProperty);
511     paintProperty->UpdateColor(theme->GetProgressColor());
512     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
513 }
514 
UpdateLoadingMarginTop(float top)515 void RefreshPattern::UpdateLoadingMarginTop(float top)
516 {
517     if (LessNotEqual(top, 0.0)) {
518         return;
519     }
520     CHECK_NULL_VOID(progressChild_);
521     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
522     CHECK_NULL_VOID(progressLayoutProperty);
523     MarginProperty marginProperty;
524     if (progressLayoutProperty->GetMarginProperty()) {
525         const auto& originMargin = (*progressLayoutProperty->GetMarginProperty());
526         marginProperty.left = originMargin.left;
527         marginProperty.right = originMargin.right;
528         marginProperty.bottom = originMargin.bottom;
529     }
530     marginProperty.top = CalcLength(top);
531     progressLayoutProperty->UpdateMargin(marginProperty);
532 }
533 } // namespace OHOS::Ace::NG
534