• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/scroll/scrollable.h"
17 
18 #include <chrono>
19 
20 #include "base/log/ace_trace.h"
21 #include "base/log/frame_report.h"
22 #include "base/log/log.h"
23 #include "base/ressched/ressched_report.h"
24 #include "base/utils/time_util.h"
25 #include "core/common/container.h"
26 #include "core/event/ace_events.h"
27 #include "core/common/layout_inspector.h"
28 
29 namespace OHOS::Ace {
30 namespace {
31 
32 constexpr double SPRING_SCROLL_MASS = 0.5;
33 constexpr double SPRING_SCROLL_STIFFNESS = 100.0;
34 constexpr double SPRING_SCROLL_DAMPING = 15.55635;
35 const RefPtr<SpringProperty> DEFAULT_OVER_SPRING_PROPERTY =
36     AceType::MakeRefPtr<SpringProperty>(SPRING_SCROLL_MASS, SPRING_SCROLL_STIFFNESS, SPRING_SCROLL_DAMPING);
37 #ifndef WEARABLE_PRODUCT
38 constexpr double FRICTION = 0.6;
39 constexpr double VELOCITY_SCALE = 1.0;
40 constexpr double MAX_VELOCITY = 800000.0;
41 constexpr double MIN_VELOCITY = -800000.0;
42 constexpr double ADJUSTABLE_VELOCITY = 3000.0;
43 #else
44 constexpr double DISTANCE_EPSILON = 1.0;
45 constexpr double FRICTION = 0.9;
46 constexpr double VELOCITY_SCALE = 0.8;
47 constexpr double MAX_VELOCITY = 5000.0;
48 constexpr double MIN_VELOCITY = -5000.0;
49 constexpr double ADJUSTABLE_VELOCITY = 0.0;
50 #endif
51 
52 #ifdef OHOS_PLATFORM
53 constexpr int64_t INCREASE_CPU_TIME_ONCE = 4000000000; // 4s(unit: ns)
54 #endif
55 
56 } // namespace
57 
58 // Static Functions.
59 double Scrollable::sFriction_ = FRICTION;
60 double Scrollable::sVelocityScale_ = VELOCITY_SCALE;
61 
SetVelocityScale(double sVelocityScale)62 void Scrollable::SetVelocityScale(double sVelocityScale)
63 {
64     if (LessOrEqual(sVelocityScale, 0.0)) {
65         LOGW("Invalid velocity scale: %{public}lf", sVelocityScale);
66         return;
67     }
68     sVelocityScale_ = sVelocityScale;
69 }
70 
SetFriction(double sFriction)71 void Scrollable::SetFriction(double sFriction)
72 {
73     if (LessOrEqual(sFriction, 0.0)) {
74         LOGW("Invalid friction value: %{public}lf", sFriction);
75         return;
76     }
77     sFriction_ = sFriction;
78 }
79 
~Scrollable()80 Scrollable::~Scrollable()
81 {
82     // If animation still runs, force stop it.
83     controller_->Stop();
84     springController_->Stop();
85 }
86 
OnFlushTouchEventsBegin()87 void Scrollable::OnFlushTouchEventsBegin()
88 {
89     if (panRecognizer_) {
90         panRecognizer_->OnFlushTouchEventsBegin();
91     }
92 }
93 
OnFlushTouchEventsEnd()94 void Scrollable::OnFlushTouchEventsEnd()
95 {
96     if (panRecognizer_) {
97         panRecognizer_->OnFlushTouchEventsEnd();
98     }
99 }
100 
Initialize(const WeakPtr<PipelineBase> & context)101 void Scrollable::Initialize(const WeakPtr<PipelineBase>& context)
102 {
103     context_ = context;
104     PanDirection panDirection;
105     if (axis_ == Axis::VERTICAL) {
106         panDirection.type = PanDirection::VERTICAL;
107     } else {
108         panDirection.type = PanDirection::HORIZONTAL;
109     }
110 
111     auto actionStart = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
112         auto scroll = weakScroll.Upgrade();
113         if (scroll) {
114             // Send event to accessibility when scroll start.
115             auto context = scroll->GetContext().Upgrade();
116             if (context) {
117                 AccessibilityEvent scrollEvent;
118                 scrollEvent.nodeId = scroll->nodeId_;
119                 scrollEvent.eventType = "scrollstart";
120                 context->SendEventToAccessibility(scrollEvent);
121             }
122             scroll->HandleDragStart(info);
123         }
124     };
125 
126     auto actionUpdate = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
127         auto scroll = weakScroll.Upgrade();
128         if (scroll) {
129             scroll->HandleDragUpdate(info);
130         }
131     };
132 
133     auto actionEnd = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
134         auto scroll = weakScroll.Upgrade();
135         if (scroll) {
136             scroll->HandleDragEnd(info);
137             // Send event to accessibility when scroll stop.
138             auto context = scroll->GetContext().Upgrade();
139             if (context && scroll->IsStopped()) {
140                 AccessibilityEvent scrollEvent;
141                 scrollEvent.nodeId = scroll->nodeId_;
142                 scrollEvent.eventType = "scrollend";
143                 context->SendEventToAccessibility(scrollEvent);
144             }
145         }
146     };
147 
148     auto actionCancel = [weakScroll = AceType::WeakClaim(this)]() {
149         auto scroll = weakScroll.Upgrade();
150         if (!scroll) {
151             return;
152         }
153         if (scroll->dragCancelCallback_) {
154             scroll->dragCancelCallback_();
155         }
156     };
157 
158     if (Container::IsCurrentUseNewPipeline()) {
159         panRecognizerNG_ =
160             AceType::MakeRefPtr<NG::PanRecognizer>(DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE);
161 
162         panRecognizerNG_->SetOnActionStart(actionStart);
163         panRecognizerNG_->SetOnActionUpdate(actionUpdate);
164         panRecognizerNG_->SetOnActionEnd(actionEnd);
165         panRecognizerNG_->SetOnActionCancel(actionCancel);
166     } else {
167         panRecognizer_ =
168             AceType::MakeRefPtr<PanRecognizer>(context, DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE);
169         panRecognizer_->SetOnActionStart(actionStart);
170         panRecognizer_->SetOnActionUpdate(actionUpdate);
171         panRecognizer_->SetOnActionEnd(actionEnd);
172         panRecognizer_->SetOnActionCancel(actionCancel);
173     }
174 
175     // use RawRecognizer to receive next touch down event to stop animation.
176     rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
177 
178     rawRecognizer_->SetOnTouchDown([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
179         auto scroll = weakScroll.Upgrade();
180         if (scroll) {
181             scroll->HandleTouchDown();
182         }
183     });
184     rawRecognizer_->SetOnTouchUp([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
185         auto scroll = weakScroll.Upgrade();
186         if (scroll) {
187             scroll->HandleTouchUp();
188         }
189     });
190     rawRecognizer_->SetOnTouchCancel([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
191         auto scroll = weakScroll.Upgrade();
192         if (scroll) {
193             scroll->HandleTouchCancel();
194         }
195     });
196 
197     controller_ = AceType::MakeRefPtr<Animator>(context);
198     springController_ = AceType::MakeRefPtr<Animator>(context);
199 
200     spring_ = GetDefaultOverSpringProperty();
201     available_ = true;
202 }
203 
SetAxis(Axis axis)204 void Scrollable::SetAxis(Axis axis)
205 {
206     axis_ = axis;
207     PanDirection panDirection;
208     if (axis_ == Axis::VERTICAL) {
209         panDirection.type = PanDirection::VERTICAL;
210     } else {
211         panDirection.type = PanDirection::HORIZONTAL;
212     }
213     if (panRecognizer_) {
214         panRecognizer_->SetDirection(panDirection);
215     }
216     if (panRecognizerNG_) {
217         panRecognizerNG_->SetDirection(panDirection);
218     }
219 }
220 
HandleTouchDown()221 void Scrollable::HandleTouchDown()
222 {
223     LOGD("handle touch down");
224     isTouching_ = true;
225     // If animation still runs, first stop it.
226     springController_->Stop();
227     if (!controller_->IsStopped()) {
228         controller_->Stop();
229     } else {
230         // Resets values.
231         currentPos_ = 0.0;
232     }
233     LOGD("handle touch down has already stopped the animation");
234 }
235 
HandleTouchUp()236 void Scrollable::HandleTouchUp()
237 {
238     LOGD("handle touch up");
239     isTouching_ = false;
240     if (outBoundaryCallback_ && !outBoundaryCallback_()) {
241         return;
242     }
243     if (springController_->IsStopped() && scrollOverCallback_) {
244         LOGD("need scroll to boundary");
245         ProcessScrollOverCallback(0.0);
246     }
247 }
248 
HandleTouchCancel()249 void Scrollable::HandleTouchCancel()
250 {
251     LOGD("handle touch cancel");
252     isTouching_ = false;
253     if (springController_->IsStopped() && scrollOverCallback_) {
254         ProcessScrollOverCallback(0.0);
255     }
256 }
257 
IsAnimationNotRunning() const258 bool Scrollable::IsAnimationNotRunning() const
259 {
260     return !isTouching_ && !controller_->IsRunning() && !springController_->IsRunning();
261 }
262 
Idle() const263 bool Scrollable::Idle() const
264 {
265     return !isTouching_ && controller_->IsStopped() && springController_->IsStopped();
266 }
267 
IsStopped() const268 bool Scrollable::IsStopped() const
269 {
270     return (!springController_ || (springController_->IsStopped())) && (!controller_ || (controller_->IsStopped()));
271 }
272 
IsSpringStopped() const273 bool Scrollable::IsSpringStopped() const
274 {
275     return !springController_ || (springController_->IsStopped());
276 }
277 
StopScrollable()278 void Scrollable::StopScrollable()
279 {
280     if (controller_) {
281         controller_->Stop();
282     }
283     if (springController_) {
284         springController_->Stop();
285     }
286 }
287 
HandleDragStart(const OHOS::Ace::GestureEvent & info)288 void Scrollable::HandleDragStart(const OHOS::Ace::GestureEvent& info)
289 {
290     ACE_FUNCTION_TRACE();
291     const auto dragPositionInMainAxis =
292         axis_ == Axis::VERTICAL ? info.GetGlobalLocation().GetY() : info.GetGlobalLocation().GetX();
293     LOGD("HandleDragStart. LocalLocation: %{public}s, GlobalLocation: %{public}s",
294         info.GetLocalLocation().ToString().c_str(), info.GetGlobalLocation().ToString().c_str());
295 #ifdef OHOS_PLATFORM
296     // Increase the cpu frequency when sliding start.
297     auto currentTime = GetSysTimestamp();
298     auto increaseCpuTime = currentTime - startIncreaseTime_;
299     if (!moved_ || increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
300         LOGI("HandleDragStart increase cpu frequency, moved_ = %{public}d", moved_);
301         startIncreaseTime_ = currentTime;
302         ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
303         if (FrameReport::GetInstance().GetEnable()) {
304             FrameReport::GetInstance().BeginListFling();
305         }
306     }
307 #endif
308     UpdateScrollPosition(dragPositionInMainAxis, SCROLL_FROM_START);
309     RelatedEventStart();
310     auto node = scrollableNode_.Upgrade();
311     if (node) {
312         node->DispatchCancelPressAnimation();
313     }
314 }
315 
HandleDragUpdate(const GestureEvent & info)316 void Scrollable::HandleDragUpdate(const GestureEvent& info)
317 {
318     ACE_FUNCTION_TRACE();
319     if (!springController_->IsStopped() || !controller_->IsStopped()) {
320         // If animation still runs, first stop it.
321         isDragUpdateStop_ = true;
322         controller_->Stop();
323         springController_->Stop();
324         currentPos_ = 0.0;
325     }
326 #ifdef OHOS_PLATFORM
327     // Handle the case where you keep sliding past limit time(4s).
328     auto currentTime = GetSysTimestamp();
329     auto increaseCpuTime = currentTime - startIncreaseTime_;
330     if (increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
331         LOGI("HandleDragUpdate increase cpu frequency, moved_ = %{public}d", moved_);
332         startIncreaseTime_ = currentTime;
333         ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
334         if (FrameReport::GetInstance().GetEnable()) {
335             FrameReport::GetInstance().BeginListFling();
336         }
337     }
338 #endif
339     LOGD("handle drag update, offset is %{public}lf", info.GetMainDelta());
340     auto mainDelta = info.GetMainDelta();
341     if (RelatedScrollEventPrepare(Offset(0.0, mainDelta))) {
342         return;
343     }
344     ExecuteScrollBegin(mainDelta);
345     ExecuteScrollFrameBegin(mainDelta, ScrollState::SCROLL);
346     auto source = info.GetInputEventType() == InputEventType::AXIS ? SCROLL_FROM_AXIS : SCROLL_FROM_UPDATE;
347     moved_ = UpdateScrollPosition(mainDelta, source);
348 }
349 
HandleDragEnd(const GestureEvent & info)350 void Scrollable::HandleDragEnd(const GestureEvent& info)
351 {
352     LOGD("handle drag end, position is %{public}lf and %{public}lf, velocity is %{public}lf",
353         info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY(), info.GetMainVelocity());
354     controller_->ClearAllListeners();
355     springController_->ClearAllListeners();
356     isDragUpdateStop_ = false;
357     touchUp_ = false;
358     scrollPause_ = false;
359     double correctVelocity = std::clamp(info.GetMainVelocity(), MIN_VELOCITY + slipFactor_, MAX_VELOCITY - slipFactor_);
360     correctVelocity = correctVelocity * sVelocityScale_;
361     currentVelocity_ = correctVelocity;
362     if (dragEndCallback_) {
363         dragEndCallback_();
364     }
365     RelatedEventEnd();
366     if (!moved_ || info.GetInputEventType() == InputEventType::AXIS) {
367         LOGI("It is not moved now,  no need to handle drag end motion");
368         if (scrollEndCallback_) {
369             scrollEndCallback_();
370         }
371         currentVelocity_ = 0.0;
372         return;
373     }
374     if (outBoundaryCallback_ && outBoundaryCallback_() && scrollOverCallback_) {
375         ProcessScrollOverCallback(correctVelocity);
376     } else {
377         double mainPosition = GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY()));
378         LOGD("[scrollMotion]position(%{public}lf), velocity(%{public}lf)", mainPosition, correctVelocity);
379         if (motion_) {
380             motion_->Reset(sFriction_, mainPosition, correctVelocity);
381         } else {
382             motion_ = AceType::MakeRefPtr<FrictionMotion>(sFriction_, mainPosition, correctVelocity);
383             motion_->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
384                 auto scroll = weakScroll.Upgrade();
385                 if (scroll) {
386                     scroll->ProcessScrollMotion(value);
387                 }
388             });
389         }
390 
391         // change motion param when list item need to be center of screen on watch
392         FixScrollMotion(mainPosition);
393 
394         // Resets values.
395         currentPos_ = mainPosition;
396         currentVelocity_ = 0.0;
397 
398         // Starts motion.
399         controller_->ClearStopListeners();
400         controller_->AddStopListener([weak = AceType::WeakClaim(this)]() {
401             auto scroll = weak.Upgrade();
402             if (scroll) {
403                 scroll->ProcessScrollMotionStop();
404                 // Send event to accessibility when scroll stop.
405                 auto context = scroll->GetContext().Upgrade();
406                 if (context && scroll->Idle()) {
407                     AccessibilityEvent scrollEvent;
408                     scrollEvent.nodeId = scroll->nodeId_;
409                     scrollEvent.eventType = "scrollend";
410                     context->SendEventToAccessibility(scrollEvent);
411                 }
412             }
413         });
414         controller_->PlayMotion(motion_);
415     }
416 }
417 
ExecuteScrollBegin(double & mainDelta)418 void Scrollable::ExecuteScrollBegin(double& mainDelta)
419 {
420     auto context = context_.Upgrade();
421     if (!scrollBeginCallback_ || !context) {
422         return;
423     }
424 
425     ScrollInfo scrollInfo;
426     if (axis_ == Axis::VERTICAL) {
427         scrollInfo = scrollBeginCallback_(0.0_vp, Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP));
428         mainDelta = context->NormalizeToPx(scrollInfo.dy);
429     } else if (axis_ == Axis::HORIZONTAL) {
430         scrollInfo = scrollBeginCallback_(Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP), 0.0_vp);
431         mainDelta = context->NormalizeToPx(scrollInfo.dx);
432     }
433 }
434 
ExecuteScrollFrameBegin(double & mainDelta,ScrollState state)435 void Scrollable::ExecuteScrollFrameBegin(double& mainDelta, ScrollState state)
436 {
437     auto context = context_.Upgrade();
438     if (!scrollFrameBeginCallback_ || !context) {
439         return;
440     }
441 
442     auto offset = Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP);
443     auto scrollRes = scrollFrameBeginCallback_(-offset, state);
444     mainDelta = -context->NormalizeToPx(scrollRes.offset);
445 }
446 
FixScrollMotion(double position)447 void Scrollable::FixScrollMotion(double position)
448 {
449 #ifdef WEARABLE_PRODUCT
450     if (motion_ && needCenterFix_ && watchFixCallback_) {
451         double finalPoisition = watchFixCallback_(motion_->GetFinalPosition(), position);
452         LOGD("final position before fix(%{public}lf), need to fix to position(%{public}lf)",
453             motion_->GetFinalPosition(), finalPoisition);
454         if (!NearEqual(finalPoisition, motion_->GetFinalPosition(), DISTANCE_EPSILON)) {
455             double velocity = motion_->GetVelocityByFinalPosition(finalPoisition);
456             motion_->Reset(sFriction_, position, velocity);
457 
458             // fix again when velocity is less than velocity threshold
459             if (!NearEqual(finalPoisition, motion_->GetFinalPosition(), DISTANCE_EPSILON)) {
460                 velocity = motion_->GetVelocityByFinalPosition(finalPoisition, 0.0);
461                 motion_->Reset(sFriction_, position, velocity, 0.0);
462             }
463             LOGD("final position after fix (%{public}lf), ", motion_->GetFinalPosition());
464         }
465     }
466 #endif
467 };
468 
StartSpringMotion(double mainPosition,double mainVelocity,const ExtentPair & extent,const ExtentPair & initExtent)469 void Scrollable::StartSpringMotion(
470     double mainPosition, double mainVelocity, const ExtentPair& extent, const ExtentPair& initExtent)
471 {
472     LOGD("[scroll] position(%{public}lf), mainVelocity(%{public}lf), minExtent(%{public}lf), maxExtent(%{public}lf), "
473          "initMinExtent(%{public}lf), initMaxExtent(%{public}lf",
474         mainPosition, mainVelocity, extent.Leading(), extent.Trailing(), initExtent.Leading(), initExtent.Trailing());
475     scrollMotion_ = AceType::MakeRefPtr<ScrollMotion>(mainPosition, mainVelocity, extent, initExtent, spring_);
476     if (!scrollMotion_->IsValid()) {
477         LOGE("scrollMotion is invalid, no available spring motion.");
478         return;
479     }
480     scrollMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double position) {
481         auto scroll = weakScroll.Upgrade();
482         if (scroll) {
483             scroll->ProcessSpringMotion(position);
484         }
485     });
486     currentPos_ = mainPosition;
487     springController_->ClearStopListeners();
488     springController_->PlayMotion(scrollMotion_);
489     springController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
490         auto scroll = weak.Upgrade();
491         if (scroll) {
492             if (scroll->moved_ && scroll->scrollEndCallback_) {
493                 scroll->scrollEndCallback_();
494             }
495             scroll->currentVelocity_ = 0.0;
496             if (scroll->isTouching_ || scroll->isDragUpdateStop_) {
497                 return;
498             }
499             scroll->moved_ = false;
500 #ifdef OHOS_PLATFORM
501             LOGI("springController stop increase cpu frequency");
502             ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
503             if (FrameReport::GetInstance().GetEnable()) {
504                 FrameReport::GetInstance().EndListFling();
505             }
506 #endif
507             if (scroll->scrollEnd_) {
508                 scroll->scrollEnd_();
509             }
510             // Send event to accessibility when scroll stop.
511             auto context = scroll->GetContext().Upgrade();
512             if (context) {
513                 AccessibilityEvent scrollEvent;
514                 scrollEvent.nodeId = scroll->nodeId_;
515                 scrollEvent.eventType = "scrollend";
516                 context->SendEventToAccessibility(scrollEvent);
517             }
518 #if !defined(PREVIEW)
519             LayoutInspector::SupportInspector();
520 #endif
521         }
522     });
523 }
524 
ProcessScrollMotionStop()525 void Scrollable::ProcessScrollMotionStop()
526 {
527     if (!scrollPause_ && moved_ && scrollEndCallback_) {
528         scrollEndCallback_();
529     }
530 
531     // spring effect special process
532     if (scrollPause_ && scrollOverCallback_) {
533         scrollPause_ = false;
534         ProcessScrollOverCallback(currentVelocity_);
535     } else {
536         currentVelocity_ = 0.0;
537         if (isDragUpdateStop_) {
538             return;
539         }
540         moved_ = false;
541 #ifdef OHOS_PLATFORM
542         LOGI("controller stop increase cpu frequency");
543         ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
544         if (FrameReport::GetInstance().GetEnable()) {
545             FrameReport::GetInstance().EndListFling();
546         }
547 #endif
548         if (scrollEnd_) {
549             scrollEnd_();
550         }
551 #if !defined(PREVIEW)
552         LayoutInspector::SupportInspector();
553 #endif
554     }
555 }
556 
ProcessSpringMotion(double position)557 void Scrollable::ProcessSpringMotion(double position)
558 {
559     LOGD("[scroll] currentPos_(%{public}lf), position(%{public}lf)", currentPos_, position);
560     currentVelocity_ = scrollMotion_->GetCurrentVelocity();
561     if (NearEqual(currentPos_, position)) {
562         UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION_SPRING);
563     } else {
564         moved_ = UpdateScrollPosition(position - currentPos_, SCROLL_FROM_ANIMATION_SPRING);
565         if (!moved_) {
566             springController_->Stop();
567         } else if (!touchUp_) {
568             if (scrollTouchUpCallback_) {
569                 scrollTouchUpCallback_();
570             }
571             touchUp_ = true;
572         }
573     }
574     currentPos_ = position;
575 }
576 
ProcessScrollMotion(double position)577 void Scrollable::ProcessScrollMotion(double position)
578 {
579     currentVelocity_ = motion_->GetCurrentVelocity();
580     LOGD("[scrolling] position(%{public}lf), currentVelocity_(%{public}lf)", position, currentVelocity_);
581     if ((NearEqual(currentPos_, position))) {
582         UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION);
583     } else {
584         // UpdateScrollPosition return false, means reach to scroll limit.
585         auto mainDelta = position - currentPos_;
586         ExecuteScrollBegin(mainDelta);
587         ExecuteScrollFrameBegin(mainDelta, ScrollState::FLING);
588         if (!UpdateScrollPosition(mainDelta, SCROLL_FROM_ANIMATION)) {
589             controller_->Stop();
590         } else if (!touchUp_) {
591             if (scrollTouchUpCallback_) {
592                 scrollTouchUpCallback_();
593             }
594             touchUp_ = true;
595         }
596     }
597     currentPos_ = position;
598 
599     // spring effect special process
600     if (outBoundaryCallback_ && outBoundaryCallback_()) {
601         scrollPause_ = true;
602         controller_->Stop();
603     }
604 }
605 
UpdateScrollPosition(const double offset,int32_t source) const606 bool Scrollable::UpdateScrollPosition(const double offset, int32_t source) const
607 {
608     bool ret = true;
609     if (callback_) {
610         ret = callback_(offset, source);
611     }
612     return ret;
613 }
614 
ProcessScrollOverCallback(double velocity)615 void Scrollable::ProcessScrollOverCallback(double velocity)
616 {
617     if (outBoundaryCallback_ && !outBoundaryCallback_()) {
618         return;
619     }
620     // In the case of chain animation enabled, you need to switch the control point first,
621     // and then correct the offset value in notification process
622     if (notifyScrollOverCallback_) {
623         notifyScrollOverCallback_(velocity);
624     }
625     // then use corrected offset to make scroll motion.
626     if (scrollOverCallback_) {
627         scrollOverCallback_(velocity);
628     }
629 }
630 
SetSlipFactor(double SlipFactor)631 void Scrollable::SetSlipFactor(double SlipFactor)
632 {
633     slipFactor_ = std::clamp(SlipFactor, -ADJUSTABLE_VELOCITY, ADJUSTABLE_VELOCITY);
634 }
635 
GetDefaultOverSpringProperty()636 const RefPtr<SpringProperty>& Scrollable::GetDefaultOverSpringProperty()
637 {
638     return DEFAULT_OVER_SPRING_PROPERTY;
639 }
640 
641 } // namespace OHOS::Ace
642