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 "base/log/jank_frame_report.h"
19 #include "base/ressched/ressched_report.h"
20 #include "core/common/layout_inspector.h"
21 
22 namespace OHOS::Ace {
23 namespace {
24 
25 constexpr float SPRING_SCROLL_MASS = 1.0f;
26 constexpr float SPRING_SCROLL_STIFFNESS = 228.0f;
27 constexpr float SPRING_SCROLL_DAMPING = 30.0f;
28 constexpr double CAP_COEFFICIENT = 0.45;
29 constexpr int32_t FIRST_THRESHOLD = 5;
30 constexpr int32_t SECOND_THRESHOLD = 10;
31 constexpr double CAP_FIXED_VALUE = 16.0;
32 constexpr uint32_t DRAG_INTERVAL_TIME = 900;
33 const RefPtr<SpringProperty> DEFAULT_OVER_SPRING_PROPERTY =
34     AceType::MakeRefPtr<SpringProperty>(SPRING_SCROLL_MASS, SPRING_SCROLL_STIFFNESS, SPRING_SCROLL_DAMPING);
35 #ifndef WEARABLE_PRODUCT
36 constexpr double FRICTION = 0.6;
37 constexpr double VELOCITY_SCALE = 1.0;
38 constexpr double ADJUSTABLE_VELOCITY = 3000.0;
39 #else
40 constexpr double DISTANCE_EPSILON = 1.0;
41 constexpr double FRICTION = 0.9;
42 constexpr double VELOCITY_SCALE = 0.8;
43 constexpr double ADJUSTABLE_VELOCITY = 0.0;
44 #endif
45 
46 #ifdef OHOS_PLATFORM
47 constexpr int64_t INCREASE_CPU_TIME_ONCE = 4000000000; // 4s(unit: ns)
48 #endif
49 
50 } // namespace
51 
52 // Static Functions.
53 double Scrollable::sFriction_ = FRICTION;
54 double Scrollable::sVelocityScale_ = VELOCITY_SCALE;
55 
SetVelocityScale(double sVelocityScale)56 void Scrollable::SetVelocityScale(double sVelocityScale)
57 {
58     if (LessOrEqual(sVelocityScale, 0.0)) {
59         return;
60     }
61     sVelocityScale_ = sVelocityScale;
62 }
63 
SetFriction(double sFriction)64 void Scrollable::SetFriction(double sFriction)
65 {
66     if (LessOrEqual(sFriction, 0.0)) {
67         return;
68     }
69     sFriction_ = sFriction;
70 }
71 
~Scrollable()72 Scrollable::~Scrollable()
73 {
74     // If animation still runs, force stop it.
75     controller_->Stop();
76     springController_->Stop();
77     scrollSnapController_->Stop();
78 }
79 
OnFlushTouchEventsBegin()80 void Scrollable::OnFlushTouchEventsBegin()
81 {
82     if (panRecognizer_) {
83         panRecognizer_->OnFlushTouchEventsBegin();
84     }
85 }
86 
OnFlushTouchEventsEnd()87 void Scrollable::OnFlushTouchEventsEnd()
88 {
89     if (panRecognizer_) {
90         panRecognizer_->OnFlushTouchEventsEnd();
91     }
92 }
93 
Initialize(const WeakPtr<PipelineBase> & context)94 void Scrollable::Initialize(const WeakPtr<PipelineBase>& context)
95 {
96     context_ = context;
97     PanDirection panDirection;
98     if (axis_ == Axis::VERTICAL) {
99         panDirection.type = PanDirection::VERTICAL;
100     } else {
101         panDirection.type = PanDirection::HORIZONTAL;
102     }
103 
104     auto actionStart = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
105         auto scroll = weakScroll.Upgrade();
106         if (scroll) {
107             // Send event to accessibility when scroll start.
108             auto context = scroll->GetContext().Upgrade();
109             if (context) {
110                 AccessibilityEvent scrollEvent;
111                 scrollEvent.nodeId = scroll->nodeId_;
112                 scrollEvent.eventType = "scrollstart";
113                 context->SendEventToAccessibility(scrollEvent);
114             }
115             scroll->isDragging_ = true;
116             scroll->HandleDragStart(info);
117         }
118     };
119 
120     auto actionUpdate = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
121         auto scroll = weakScroll.Upgrade();
122         if (scroll) {
123             scroll->HandleDragUpdate(info);
124         }
125     };
126 
127     auto actionEnd = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
128         auto scroll = weakScroll.Upgrade();
129         if (scroll) {
130             scroll->HandleDragEnd(info);
131             // Send event to accessibility when scroll stop.
132             auto context = scroll->GetContext().Upgrade();
133             if (context && scroll->IsStopped()) {
134                 AccessibilityEvent scrollEvent;
135                 scrollEvent.nodeId = scroll->nodeId_;
136                 scrollEvent.eventType = "scrollend";
137                 context->SendEventToAccessibility(scrollEvent);
138             }
139             if (scroll->actionEnd_) {
140                 auto gestureEvent = info;
141                 scroll->actionEnd_(gestureEvent);
142             }
143             scroll->isDragging_ = false;
144         }
145     };
146 
147     auto actionCancel = [weakScroll = AceType::WeakClaim(this)]() {
148         auto scroll = weakScroll.Upgrade();
149         if (!scroll) {
150             return;
151         }
152         if (scroll->dragCancelCallback_) {
153             scroll->dragCancelCallback_();
154         }
155         scroll->isDragging_ = false;
156     };
157 
158     if (Container::IsCurrentUseNewPipeline()) {
159         panRecognizerNG_ = AceType::MakeRefPtr<NG::PanRecognizer>(
160             DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
161         panRecognizerNG_->SetIsAllowMouse(false);
162         panRecognizerNG_->SetOnActionStart(actionStart);
163         panRecognizerNG_->SetOnActionUpdate(actionUpdate);
164         panRecognizerNG_->SetOnActionEnd(actionEnd);
165         panRecognizerNG_->SetOnActionCancel(actionCancel);
166     } else {
167         panRecognizer_ = AceType::MakeRefPtr<PanRecognizer>(
168             context, DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
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_ = CREATE_ANIMATOR(context);
198     springController_ = CREATE_ANIMATOR(context);
199     scrollSnapController_ = CREATE_ANIMATOR(context);
200     snapController_ = CREATE_ANIMATOR(context);
201     snapController_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
202         auto scroll = weakScroll.Upgrade();
203         CHECK_NULL_VOID(scroll);
204         scroll->ProcessScrollMotionStop();
205         // Send event to accessibility when scroll stop.
206         auto context = scroll->GetContext().Upgrade();
207         CHECK_NULL_VOID(context && scroll->Idle());
208         AccessibilityEvent scrollEvent;
209         scrollEvent.nodeId = scroll->nodeId_;
210         scrollEvent.eventType = "scrollend";
211         context->SendEventToAccessibility(scrollEvent);
212     });
213 
214     spring_ = GetDefaultOverSpringProperty();
215     available_ = true;
216 }
217 
SetAxis(Axis axis)218 void Scrollable::SetAxis(Axis axis)
219 {
220     axis_ = axis;
221     PanDirection panDirection;
222     if (axis_ == Axis::NONE) {
223         panDirection.type = PanDirection::NONE;
224     } else if (axis_ == Axis::VERTICAL) {
225         panDirection.type = PanDirection::VERTICAL;
226     } else {
227         panDirection.type = PanDirection::HORIZONTAL;
228     }
229     if (panRecognizer_) {
230         panRecognizer_->SetDirection(panDirection);
231     }
232     if (panRecognizerNG_) {
233         panRecognizerNG_->SetDirection(panDirection);
234     }
235 }
236 
HandleTouchDown()237 void Scrollable::HandleTouchDown()
238 {
239     isTouching_ = true;
240     // If animation still runs, first stop it.
241     springController_->Stop();
242     if (!controller_->IsStopped()) {
243         controller_->Stop();
244     } else if (snapController_->IsRunning()) {
245         snapController_->Stop();
246     } else {
247         // Resets values.
248         currentPos_ = 0.0;
249     }
250     if (!scrollSnapController_->IsStopped()) {
251         scrollSnapController_->Stop();
252     }
253 }
254 
HandleTouchUp()255 void Scrollable::HandleTouchUp()
256 {
257     // Two fingers are alternately drag, one finger is released without triggering spring animation.
258     if (isDragging_) {
259         return;
260     }
261     isTouching_ = false;
262     if (outBoundaryCallback_ && !outBoundaryCallback_()) {
263         if (scrollSnapController_->IsStopped() && scrollSnapCallback_) {
264             scrollSnapCallback_(0.0, 0.0);
265         }
266         return;
267     }
268     if (springController_->IsStopped() && scrollOverCallback_) {
269         ProcessScrollOverCallback(0.0);
270     }
271 }
272 
HandleTouchCancel()273 void Scrollable::HandleTouchCancel()
274 {
275     isTouching_ = false;
276     if (springController_->IsStopped() && scrollOverCallback_) {
277         ProcessScrollOverCallback(0.0);
278     }
279 }
280 
IsAnimationNotRunning() const281 bool Scrollable::IsAnimationNotRunning() const
282 {
283     return !isTouching_ && !controller_->IsRunning() && !springController_->IsRunning() &&
284            !scrollSnapController_->IsRunning();
285 }
286 
Idle() const287 bool Scrollable::Idle() const
288 {
289     return !isTouching_ && (controller_->IsStopped() || controller_->GetStatus() == Animator::Status::IDLE) &&
290            (springController_->IsStopped() || springController_->GetStatus() == Animator::Status::IDLE) &&
291            (scrollSnapController_->IsStopped() || scrollSnapController_->GetStatus() == Animator::Status::IDLE) &&
292            (snapController_->IsStopped() || snapController_->GetStatus() == Animator::Status::IDLE);
293 }
294 
IsStopped() const295 bool Scrollable::IsStopped() const
296 {
297     return (!springController_ || (springController_->IsStopped()) ||
298                (springController_->GetStatus() == Animator::Status::IDLE)) &&
299            (!controller_ || (controller_->IsStopped()) || (controller_->GetStatus() == Animator::Status::IDLE)) &&
300            (!scrollSnapController_ || (scrollSnapController_->IsStopped()) ||
301                (scrollSnapController_->GetStatus() == Animator::Status::IDLE)) &&
302            (!snapController_ || (snapController_->IsStopped()) ||
303                (snapController_->GetStatus() == Animator::Status::IDLE));
304 }
305 
IsSpringStopped() const306 bool Scrollable::IsSpringStopped() const
307 {
308     return !springController_ || (springController_->IsStopped());
309 }
310 
IsSnapStopped() const311 bool Scrollable::IsSnapStopped() const
312 {
313     return !snapController_ || (snapController_->IsStopped()) ||
314            (snapController_->GetStatus() == Animator::Status::IDLE);
315 }
316 
StopScrollable()317 void Scrollable::StopScrollable()
318 {
319     if (controller_) {
320         controller_->Stop();
321     }
322     if (springController_) {
323         springController_->Stop();
324     }
325     if (scrollSnapController_) {
326         scrollSnapController_->Stop();
327     }
328     if (snapController_) {
329         snapController_->Stop();
330     }
331 }
332 
HandleScrollEnd()333 void Scrollable::HandleScrollEnd()
334 {
335     // priority:
336     //  1. onScrollEndRec_ (would internally call onScrollEnd)
337     //  2. scrollEndCallback_
338     if (onScrollEndRec_) {
339         onScrollEndRec_();
340         return;
341     }
342     if (scrollEndCallback_) {
343         scrollEndCallback_();
344     }
345 }
346 
HandleDragStart(const OHOS::Ace::GestureEvent & info)347 void Scrollable::HandleDragStart(const OHOS::Ace::GestureEvent& info)
348 {
349     ACE_FUNCTION_TRACE();
350     currentVelocity_ = info.GetMainVelocity();
351     if (dragFRCSceneCallback_) {
352         dragFRCSceneCallback_(currentVelocity_, NG::SceneStatus::START);
353     }
354     if (continuousDragStatus_) {
355         IncreaseContinueDragCount();
356         task_.Cancel();
357     }
358     SetDragStartPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
359     const auto dragPositionInMainAxis =
360         axis_ == Axis::VERTICAL ? info.GetGlobalLocation().GetY() : info.GetGlobalLocation().GetX();
361 #ifdef OHOS_PLATFORM
362     // Increase the cpu frequency when sliding start.
363     auto currentTime = GetSysTimestamp();
364     auto increaseCpuTime = currentTime - startIncreaseTime_;
365     if (!moved_ || increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
366         startIncreaseTime_ = currentTime;
367         ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
368         if (FrameReport::GetInstance().GetEnable()) {
369             FrameReport::GetInstance().BeginListFling();
370         }
371     }
372 #endif
373     JankFrameReport::GetInstance().SetFrameJankFlag(JANK_RUNNING_SCROLL);
374     if (onScrollStartRec_) {
375         onScrollStartRec_(static_cast<float>(dragPositionInMainAxis));
376     }
377     RelatedEventStart();
378     auto node = scrollableNode_.Upgrade();
379     if (node) {
380         node->DispatchCancelPressAnimation();
381     }
382 }
383 
HandleScrollParentFirst(double & offset,int32_t source,NestedState state)384 [[deprecated("using NestableScrollContainer")]] ScrollResult Scrollable::HandleScrollParentFirst(
385     double& offset, int32_t source, NestedState state)
386 {
387     auto parent = parent_.Upgrade();
388     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
389     if (state == NestedState::CHILD_OVER_SCROLL) {
390         if (edgeEffect_ == EdgeEffect::NONE) {
391             return parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
392         }
393         ExecuteScrollFrameBegin(offset, scrollState);
394         return { 0, true };
395     }
396     auto result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL);
397     offset = result.remain;
398     if (NearZero(offset)) {
399         canOverScroll_ = false;
400         return { 0, false };
401     }
402     double allOffset = offset;
403     ExecuteScrollFrameBegin(offset, scrollState);
404     auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
405     auto overOffsets = overScrollOffsetCallback_(offset);
406     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
407     remainOffset += overOffset;
408     if (NearZero(remainOffset)) {
409         canOverScroll_ = false;
410         return { 0, false };
411     }
412     if (state == NestedState::CHILD_SCROLL) {
413         offset -= overOffset;
414         canOverScroll_ = false;
415         return { remainOffset, !NearZero(overOffset) };
416     }
417     if (edgeEffect_ == EdgeEffect::NONE) {
418         result = parent->HandleScroll(remainOffset, source, NestedState::CHILD_OVER_SCROLL);
419     }
420     canOverScroll_ = !NearZero(overOffset) || (NearZero(offset) && result.reachEdge);
421     return { 0, canOverScroll_ };
422 }
423 
HandleScrollSelfFirst(double & offset,int32_t source,NestedState state)424 [[deprecated("using NestableScrollContainer")]] ScrollResult Scrollable::HandleScrollSelfFirst(
425     double& offset, int32_t source, NestedState state)
426 {
427     auto parent = parent_.Upgrade();
428     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
429     if (state == NestedState::CHILD_OVER_SCROLL) {
430         auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
431         if (NearZero(result.remain)) {
432             offset = 0;
433             return result;
434         }
435         ExecuteScrollFrameBegin(offset, scrollState);
436         if (edgeEffect_ == EdgeEffect::NONE) {
437             return result;
438         }
439         return { 0, true };
440     }
441     double allOffset = offset;
442     ExecuteScrollFrameBegin(offset, scrollState);
443     auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
444     auto overOffsets = overScrollOffsetCallback_(offset);
445     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
446     if (NearZero(overOffset) && NearZero(remainOffset)) {
447         canOverScroll_ = false;
448         return { 0, false };
449     }
450     offset -= overOffset;
451     auto result = parent->HandleScroll(overOffset + remainOffset, source, NestedState::CHILD_SCROLL);
452     if (NearZero(result.remain)) {
453         canOverScroll_ = false;
454         return { 0, false };
455     }
456     if (state == NestedState::CHILD_SCROLL) {
457         canOverScroll_ = false;
458         return result;
459     }
460     auto overRes = parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL);
461     offset += std::abs(overOffset) < std::abs(result.remain) ? overOffset : overRes.remain;
462     canOverScroll_ = (!NearZero(overOffset) || NearZero(offset)) && overRes.reachEdge;
463     return { 0, canOverScroll_ };
464 }
465 
HandleScrollSelfOnly(double & offset,int32_t source,NestedState state)466 [[deprecated("using NestableScrollContainer")]] ScrollResult Scrollable::HandleScrollSelfOnly(
467     double& offset, int32_t source, NestedState state)
468 {
469     double allOffset = offset;
470     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
471     ExecuteScrollFrameBegin(offset, scrollState);
472     auto remainOffset = allOffset - offset;
473     auto overOffsets = overScrollOffsetCallback_(offset);
474     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
475     remainOffset += overOffset;
476     if (NearZero(remainOffset)) {
477         canOverScroll_ = false;
478         return { 0, false };
479     }
480     bool canOverScroll = false;
481     if (state == NestedState::CHILD_SCROLL) {
482         offset -= overOffset;
483     } else if (state == NestedState::GESTURE) {
484         canOverScroll = !NearZero(overOffset) && edgeEffect_ != EdgeEffect::NONE;
485     } else if (edgeEffect_ != EdgeEffect::NONE) {
486         remainOffset = 0;
487     }
488     canOverScroll_ = canOverScroll;
489     return { remainOffset, !NearZero(overOffset) };
490 }
491 
HandleScrollParallel(double & offset,int32_t source,NestedState state)492 [[deprecated("using NestableScrollContainer")]] ScrollResult Scrollable::HandleScrollParallel(
493     double& offset, int32_t source, NestedState state)
494 {
495     auto remainOffset = 0.0;
496     auto parent = parent_.Upgrade();
497     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
498     if (state == NestedState::CHILD_OVER_SCROLL) {
499         if (edgeEffect_ == EdgeEffect::NONE) {
500             auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
501             remainOffset = result.remain;
502             offset = 0;
503         } else {
504             ExecuteScrollFrameBegin(offset, scrollState);
505         }
506         return { remainOffset, true };
507     }
508 
509     bool canOverScroll = false;
510     double parentOffset = offset;
511     ExecuteScrollFrameBegin(offset, scrollState);
512     auto result = parent->HandleScroll(parentOffset, source, NestedState::CHILD_SCROLL);
513 
514     auto overOffsets = overScrollOffsetCallback_(offset);
515     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
516     if (!NearZero(overOffset) && result.reachEdge) {
517         if (state == NestedState::CHILD_SCROLL) {
518             remainOffset = overOffset;
519             offset = offset - overOffset;
520         } else if (edgeEffect_ == EdgeEffect::NONE) {
521             parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL);
522             canOverScroll = true;
523             offset = offset - overOffset;
524         } else {
525             canOverScroll = true;
526         }
527     } else if (!NearZero(overOffset)) {
528         offset = offset - overOffset;
529     }
530     canOverScroll_ = canOverScroll;
531     return { remainOffset, !NearZero(overOffset) && result.reachEdge };
532 }
533 
HandleScroll(double offset,int32_t source,NestedState state)534 ScrollResult Scrollable::HandleScroll(double offset, int32_t source, NestedState state)
535 {
536     if (!handleScrollCallback_) {
537         ExecuteScrollBegin(offset);
538         moved_ = UpdateScrollPosition(offset, source);
539         canOverScroll_ = false;
540         return { 0, false };
541     }
542     // call NestableScrollContainer::HandleScroll
543     return handleScrollCallback_(static_cast<float>(offset), source, state);
544 }
545 
HandleDragUpdate(const GestureEvent & info)546 void Scrollable::HandleDragUpdate(const GestureEvent& info)
547 {
548     ACE_FUNCTION_TRACE();
549     currentVelocity_ = info.GetMainVelocity();
550     if (dragFRCSceneCallback_) {
551         dragFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
552     }
553     if (!NearZero(info.GetMainVelocity()) && dragCount_ >= FIRST_THRESHOLD) {
554         if (Negative(lastVelocity_ / info.GetMainVelocity())) {
555             ResetContinueDragCount();
556         }
557     }
558     if (!springController_->IsStopped() || !controller_->IsStopped() || !scrollSnapController_->IsStopped() ||
559         !snapController_->IsStopped()) {
560         // If animation still runs, first stop it.
561         isDragUpdateStop_ = true;
562         controller_->Stop();
563         springController_->Stop();
564         scrollSnapController_->Stop();
565         snapController_->Stop();
566         currentPos_ = 0.0;
567     }
568 #ifdef OHOS_PLATFORM
569     // Handle the case where you keep sliding past limit time(4s).
570     auto currentTime = GetSysTimestamp();
571     auto increaseCpuTime = currentTime - startIncreaseTime_;
572     if (increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
573         startIncreaseTime_ = currentTime;
574         ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
575         if (FrameReport::GetInstance().GetEnable()) {
576             FrameReport::GetInstance().BeginListFling();
577         }
578     }
579 #endif
580     auto mainDelta = info.GetMainDelta();
581     if (RelatedScrollEventPrepare(Offset(0.0, mainDelta))) {
582         return;
583     }
584     JankFrameReport::GetInstance().RecordFrameUpdate();
585     auto source = info.GetInputEventType() == InputEventType::AXIS ? SCROLL_FROM_AXIS : SCROLL_FROM_UPDATE;
586     HandleScroll(mainDelta, source, NestedState::GESTURE);
587 }
588 
HandleDragEnd(const GestureEvent & info)589 void Scrollable::HandleDragEnd(const GestureEvent& info)
590 {
591     if (dragFRCSceneCallback_) {
592         dragFRCSceneCallback_(info.GetMainVelocity(), NG::SceneStatus::END);
593     }
594     isTouching_ = false;
595     controller_->ClearAllListeners();
596     springController_->ClearAllListeners();
597     scrollSnapController_->ClearAllListeners();
598     isDragUpdateStop_ = false;
599     touchUp_ = false;
600     scrollPause_ = false;
601     lastVelocity_ = info.GetMainVelocity();
602     double correctVelocity =
603         std::clamp(info.GetMainVelocity(), -maxFlingVelocity_ + slipFactor_, maxFlingVelocity_ - slipFactor_);
604     SetDragEndPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
605     correctVelocity = correctVelocity * sVelocityScale_ * GetGain(GetDragOffset());
606     currentVelocity_ = correctVelocity;
607 
608     lastPos_ = GetDragOffset();
609     JankFrameReport::GetInstance().ClearFrameJankFlag(JANK_RUNNING_SCROLL);
610     if (dragEndCallback_) {
611         dragEndCallback_();
612     }
613     RelatedEventEnd();
614     double mainPosition = GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY()));
615     if (!moved_ || info.GetInputEventType() == InputEventType::AXIS) {
616         if (calePredictSnapOffsetCallback_) {
617             std::optional<float> predictSnapOffset = calePredictSnapOffsetCallback_(0.0f, 0.0f, 0.0f);
618             if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
619                 currentPos_ = mainPosition;
620                 ProcessScrollSnapSpringMotion(predictSnapOffset.value(), correctVelocity);
621                 return;
622             }
623         }
624         HandleScrollEnd();
625         currentVelocity_ = 0.0;
626 #ifdef OHOS_PLATFORM
627         ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
628         if (FrameReport::GetInstance().GetEnable()) {
629             FrameReport::GetInstance().EndListFling();
630         }
631 #endif
632     } else if (!Container::IsCurrentUseNewPipeline() && outBoundaryCallback_ && outBoundaryCallback_() &&
633                scrollOverCallback_) {
634         ResetContinueDragCount();
635         ProcessScrollOverCallback(correctVelocity);
636     } else if (canOverScroll_) {
637         ResetContinueDragCount();
638         HandleOverScroll(correctVelocity);
639     } else {
640         StartScrollAnimation(mainPosition, correctVelocity);
641     }
642     SetDelayedTask();
643 }
644 
StartScrollAnimation(float mainPosition,float correctVelocity)645 void Scrollable::StartScrollAnimation(float mainPosition, float correctVelocity)
646 {
647     if (springController_ && !springController_->IsStopped()) {
648         springController_->Stop();
649     }
650     StopSnapController();
651     double friction = friction_ > 0 ? friction_ : sFriction_;
652     if (motion_) {
653         motion_->Reset(friction, mainPosition, correctVelocity);
654     } else {
655         motion_ =
656             AceType::MakeRefPtr<FrictionMotion>(friction, mainPosition, correctVelocity);
657         motion_->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
658             auto scroll = weakScroll.Upgrade();
659             if (scroll) {
660                 scroll->ProcessScrollMotion(value);
661             }
662         });
663     }
664     if (calePredictSnapOffsetCallback_) {
665         std::optional<float> predictSnapOffset =
666           calePredictSnapOffsetCallback_(motion_->GetFinalPosition() - mainPosition, GetDragOffset(), correctVelocity);
667         if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
668             currentPos_ = mainPosition;
669             ProcessScrollSnapSpringMotion(predictSnapOffset.value(), correctVelocity);
670             return;
671         }
672     }
673     if (scrollSnapCallback_ && scrollSnapCallback_(motion_->GetFinalPosition() - mainPosition, correctVelocity)) {
674         currentVelocity_ = 0.0;
675         return;
676     }
677     // change motion param when list item need to be center of screen on watch
678     FixScrollMotion(mainPosition);
679     // Resets values.
680     currentPos_ = mainPosition;
681     currentVelocity_ = 0.0;
682     // Starts motion.
683     controller_->ClearStopListeners();
684     controller_->AddStopListener([weak = AceType::WeakClaim(this)]() {
685         auto scroll = weak.Upgrade();
686         if (scroll) {
687             scroll->ProcessScrollMotionStop();
688             // Send event to accessibility when scroll stop.
689             auto context = scroll->GetContext().Upgrade();
690             if (context && scroll->Idle()) {
691                 AccessibilityEvent scrollEvent;
692                 scrollEvent.nodeId = scroll->nodeId_;
693                 scrollEvent.eventType = "scrollend";
694                 context->SendEventToAccessibility(scrollEvent);
695             }
696         }
697     });
698     if (scrollMotionFRCSceneCallback_) {
699         scrollMotionFRCSceneCallback_(motion_->GetCurrentVelocity(), NG::SceneStatus::START);
700     }
701     controller_->PlayMotion(motion_);
702 }
703 
SetDelayedTask()704 void Scrollable::SetDelayedTask()
705 {
706     SetContinuousDragStatus(true);
707     auto context = PipelineContext::GetCurrentContext();
708     CHECK_NULL_VOID(context);
709     auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
710     task_.Reset([weak = WeakClaim(this)] {
711         auto drag = weak.Upgrade();
712         if (drag) {
713             drag->ResetContinueDragCount();
714             drag->SetContinuousDragStatus(false);
715         }
716     });
717     taskExecutor.PostDelayedTask(task_, DRAG_INTERVAL_TIME, "ArkUIScrollDragInterval");
718 }
719 
ComputeCap(int dragCount)720 double Scrollable::ComputeCap(int dragCount)
721 {
722     if (dragCount < FIRST_THRESHOLD) {
723         return 1.0;
724     }
725     auto cap = ComputeCap(dragCount - 1) + CAP_COEFFICIENT * (dragCount - 1);
726     return cap;
727 }
728 
GetGain(double delta)729 double Scrollable::GetGain(double delta)
730 {
731     auto cap = 1.0;
732     auto gain = 1.0;
733     if (!continuousSlidingCallback_) {
734         return gain;
735     }
736     auto screenHeight = continuousSlidingCallback_();
737     if (delta == 0 || screenHeight == 0) {
738         return gain;
739     }
740     if (dragCount_ >= FIRST_THRESHOLD && dragCount_ < SECOND_THRESHOLD) {
741         if (Negative(lastPos_ / delta)) {
742             ResetContinueDragCount();
743             return gain;
744         }
745         cap = ComputeCap(dragCount_);
746         gain = (LessNotEqual(cap, std::abs(delta) / screenHeight * (dragCount_ - 1))) ? cap :
747             std::abs(delta) / screenHeight * (dragCount_ - 1);
748     } else if (dragCount_ >= SECOND_THRESHOLD) {
749         if (Negative(lastPos_ / delta)) {
750             ResetContinueDragCount();
751             return gain;
752         }
753         cap = CAP_FIXED_VALUE;
754         gain = (LessNotEqual(cap, std::abs(delta) / screenHeight * (dragCount_ - 1))) ? cap :
755             std::abs(delta) / screenHeight * (dragCount_ - 1);
756     }
757     return gain;
758 }
759 
ExecuteScrollBegin(double & mainDelta)760 void Scrollable::ExecuteScrollBegin(double& mainDelta)
761 {
762     auto context = context_.Upgrade();
763     if (!scrollBeginCallback_ || !context) {
764         return;
765     }
766 
767     ScrollInfo scrollInfo;
768     if (axis_ == Axis::VERTICAL) {
769         scrollInfo = scrollBeginCallback_(0.0_vp, Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP));
770         mainDelta = context->NormalizeToPx(scrollInfo.dy);
771     } else if (axis_ == Axis::HORIZONTAL) {
772         scrollInfo = scrollBeginCallback_(Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP), 0.0_vp);
773         mainDelta = context->NormalizeToPx(scrollInfo.dx);
774     }
775 }
776 
ExecuteScrollFrameBegin(double & mainDelta,ScrollState state)777 [[deprecated]] void Scrollable::ExecuteScrollFrameBegin(double& mainDelta, ScrollState state)
778 {
779     auto context = context_.Upgrade();
780     if (!scrollFrameBeginCallback_ || !context) {
781         return;
782     }
783 
784     auto offset = Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP);
785     auto scrollRes = scrollFrameBeginCallback_(-offset, state);
786     mainDelta = -context->NormalizeToPx(scrollRes.offset);
787 }
788 
FixScrollMotion(double position)789 void Scrollable::FixScrollMotion(double position)
790 {
791 #ifdef WEARABLE_PRODUCT
792     if (motion_ && needCenterFix_ && watchFixCallback_) {
793         double finalPoisition = watchFixCallback_(motion_->GetFinalPosition(), position);
794         if (!NearEqual(finalPoisition, motion_->GetFinalPosition(), DISTANCE_EPSILON)) {
795             double velocity = motion_->GetVelocityByFinalPosition(finalPoisition);
796             double friction = friction_ > 0 ? friction_ : sFriction_;
797             motion_->Reset(friction, position, velocity);
798 
799             // fix again when velocity is less than velocity threshold
800             if (!NearEqual(finalPoisition, motion_->GetFinalPosition(), DISTANCE_EPSILON)) {
801                 velocity = motion_->GetVelocityByFinalPosition(finalPoisition, 0.0);
802                 motion_->Reset(friction, position, velocity, 0.0);
803             }
804         }
805     }
806 #endif
807 };
808 
StartScrollSnapMotion(float predictSnapOffset,float scrollSnapVelocity)809 void Scrollable::StartScrollSnapMotion(float predictSnapOffset, float scrollSnapVelocity)
810 {
811     auto start = currentPos_;
812     auto end = currentPos_ + predictSnapOffset;
813     scrollSnapMotion_ = AceType::MakeRefPtr<SpringMotion>(start, end, scrollSnapVelocity, DEFAULT_OVER_SPRING_PROPERTY);
814 
815     scrollSnapMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double position) {
816         auto scroll = weakScroll.Upgrade();
817         if (scroll) {
818             scroll->ProcessScrollSnapMotion(position);
819         }
820     });
821     scrollSnapController_->ClearStopListeners();
822     scrollSnapController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
823         auto scroll = weak.Upgrade();
824         CHECK_NULL_VOID(scroll);
825         scroll->ProcessScrollSnapStop();
826     });
827     if (scrollMotionFRCSceneCallback_) {
828         scrollMotionFRCSceneCallback_(scrollSnapMotion_->GetCurrentVelocity(), NG::SceneStatus::START);
829     }
830     scrollSnapController_->PlayMotion(scrollSnapMotion_);
831 }
832 
ProcessScrollSnapSpringMotion(float scrollSnapDelta,float scrollSnapVelocity)833 void Scrollable::ProcessScrollSnapSpringMotion(float scrollSnapDelta, float scrollSnapVelocity)
834 {
835     if (!snapController_) {
836         snapController_ = AceType::MakeRefPtr<Animator>(PipelineBase::GetCurrentContext());
837         snapController_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
838             auto scroll = weakScroll.Upgrade();
839             CHECK_NULL_VOID(scroll);
840             scroll->ProcessScrollMotionStop();
841             // Send event to accessibility when scroll stop.
842             auto context = scroll->GetContext().Upgrade();
843             CHECK_NULL_VOID(context && scroll->Idle());
844             AccessibilityEvent scrollEvent;
845             scrollEvent.nodeId = scroll->nodeId_;
846             scrollEvent.eventType = "scrollend";
847             context->SendEventToAccessibility(scrollEvent);
848         });
849     }
850     if (!snapMotion_) {
851         snapMotion_ = AceType::MakeRefPtr<SpringMotion>(
852             currentPos_, scrollSnapDelta + currentPos_, scrollSnapVelocity, DEFAULT_OVER_SPRING_PROPERTY);
853         snapMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](float position) {
854             auto scroll = weakScroll.Upgrade();
855             if (scroll) {
856                 scroll->ProcessScrollMotion(position);
857             }
858         });
859     } else {
860         snapMotion_->Reset(
861             currentPos_, scrollSnapDelta + currentPos_, scrollSnapVelocity, DEFAULT_OVER_SPRING_PROPERTY);
862     }
863     if (scrollMotionFRCSceneCallback_) {
864         scrollMotionFRCSceneCallback_(snapMotion_->GetCurrentVelocity(), NG::SceneStatus::START);
865     }
866     snapController_->PlayMotion(snapMotion_);
867 }
868 
UpdateScrollSnapStartOffset(double offset)869 void Scrollable::UpdateScrollSnapStartOffset(double offset)
870 {
871     if (scrollSnapMotion_ && scrollSnapController_ && scrollSnapController_->IsRunning()) {
872         scrollSnapController_->ClearStopListeners();
873         scrollSnapController_->Stop();
874         auto currPos = scrollSnapMotion_->GetCurrentPosition();
875         auto endPos = scrollSnapMotion_->GetEndValue();
876         auto velocity = scrollSnapMotion_->GetCurrentVelocity();
877         scrollSnapMotion_->Reset(currPos + offset, endPos, velocity, DEFAULT_OVER_SPRING_PROPERTY);
878         scrollSnapController_->PlayMotion(scrollSnapMotion_);
879         scrollSnapController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
880             auto scroll = weak.Upgrade();
881             CHECK_NULL_VOID(scroll);
882             scroll->ProcessScrollSnapStop();
883         });
884     }
885 }
886 
ProcessScrollSnapMotion(double position)887 void Scrollable::ProcessScrollSnapMotion(double position)
888 {
889     currentVelocity_ = scrollSnapMotion_->GetCurrentVelocity();
890     if (scrollMotionFRCSceneCallback_) {
891         scrollMotionFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
892     }
893     if (NearEqual(currentPos_, position)) {
894         UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION_SPRING);
895     } else {
896         auto mainDelta = position - currentPos_;
897         HandleScroll(mainDelta, SCROLL_FROM_ANIMATION, NestedState::GESTURE);
898         if (!moved_) {
899             scrollSnapController_->Stop();
900         } else if (!touchUp_) {
901             if (scrollTouchUpCallback_) {
902                 scrollTouchUpCallback_();
903             }
904             touchUp_ = true;
905         }
906     }
907     currentPos_ = scrollSnapMotion_->GetCurrentPosition();
908     if (outBoundaryCallback_ && outBoundaryCallback_()) {
909         scrollPause_ = true;
910         scrollSnapController_->Stop();
911     }
912 }
913 
ProcessScrollSnapStop()914 void Scrollable::ProcessScrollSnapStop()
915 {
916     if (scrollSnapMotion_ && scrollMotionFRCSceneCallback_) {
917         scrollMotionFRCSceneCallback_(scrollSnapMotion_->GetCurrentVelocity(), NG::SceneStatus::END);
918     }
919     if (scrollPause_) {
920         scrollPause_ = false;
921         HandleOverScroll(currentVelocity_);
922     } else {
923         OnAnimateStop();
924     }
925 }
926 
OnAnimateStop()927 void Scrollable::OnAnimateStop()
928 {
929     if (scrollMotion_ && scrollMotion_->IsValid() && scrollMotionFRCSceneCallback_) {
930         scrollMotionFRCSceneCallback_(scrollMotion_->GetCurrentVelocity(), NG::SceneStatus::END);
931     }
932     if (moved_) {
933         HandleScrollEnd();
934     }
935     currentVelocity_ = 0.0;
936     if (isTouching_ || isDragUpdateStop_) {
937         return;
938     }
939     moved_ = false;
940 #ifdef OHOS_PLATFORM
941     ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
942     if (FrameReport::GetInstance().GetEnable()) {
943         FrameReport::GetInstance().EndListFling();
944     }
945 #endif
946     if (scrollEnd_) {
947         scrollEnd_();
948     }
949     // Send event to accessibility when scroll stop.
950     auto context = GetContext().Upgrade();
951     if (context) {
952         AccessibilityEvent scrollEvent;
953         scrollEvent.nodeId = nodeId_;
954         scrollEvent.eventType = "scrollend";
955         context->SendEventToAccessibility(scrollEvent);
956     }
957 #if !defined(PREVIEW)
958     LayoutInspector::SupportInspector();
959 #endif
960 }
961 
StartSpringMotion(double mainPosition,double mainVelocity,const ExtentPair & extent,const ExtentPair & initExtent)962 void Scrollable::StartSpringMotion(
963     double mainPosition, double mainVelocity, const ExtentPair& extent, const ExtentPair& initExtent)
964 {
965     scrollMotion_ = AceType::MakeRefPtr<ScrollMotion>(mainPosition, mainVelocity, extent, initExtent, spring_);
966     if (!scrollMotion_->IsValid()) {
967         return;
968     }
969     scrollMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double position) {
970         auto scroll = weakScroll.Upgrade();
971         if (scroll) {
972             scroll->ProcessSpringMotion(position);
973         }
974     });
975     currentPos_ = mainPosition;
976     springController_->ClearStopListeners();
977     if (scrollMotionFRCSceneCallback_) {
978         scrollMotionFRCSceneCallback_(scrollMotion_->GetCurrentVelocity(), NG::SceneStatus::START);
979     }
980     springController_->PlayMotion(scrollMotion_);
981     springController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
982         auto scroll = weak.Upgrade();
983         CHECK_NULL_VOID(scroll);
984         scroll->OnAnimateStop();
985     });
986 }
987 
ProcessScrollMotionStop()988 void Scrollable::ProcessScrollMotionStop()
989 {
990     if (motion_ && scrollMotionFRCSceneCallback_) {
991         scrollMotionFRCSceneCallback_(motion_->GetCurrentVelocity(), NG::SceneStatus::END);
992     }
993     if (snapMotion_ && scrollMotionFRCSceneCallback_) {
994         scrollMotionFRCSceneCallback_(snapMotion_->GetCurrentVelocity(), NG::SceneStatus::END);
995     }
996     if (needScrollSnapChange_ && calePredictSnapOffsetCallback_ && motion_) {
997         needScrollSnapChange_ = false;
998         auto predictSnapOffset = calePredictSnapOffsetCallback_(motion_->GetFinalPosition() - currentPos_, 0.0f, 0.0f);
999         if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
1000             ProcessScrollSnapSpringMotion(predictSnapOffset.value(), currentVelocity_);
1001             return;
1002         }
1003     }
1004     // spring effect special process
1005     if (scrollPause_) {
1006         scrollPause_ = false;
1007         HandleOverScroll(currentVelocity_);
1008     } else {
1009         if (isDragUpdateStop_) {
1010             return;
1011         }
1012         moved_ = false;
1013         HandleScrollEnd();
1014 #ifdef OHOS_PLATFORM
1015         ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
1016         if (FrameReport::GetInstance().GetEnable()) {
1017             FrameReport::GetInstance().EndListFling();
1018         }
1019 #endif
1020         if (scrollEnd_) {
1021             scrollEnd_();
1022         }
1023         currentVelocity_ = 0.0;
1024 #if !defined(PREVIEW)
1025         LayoutInspector::SupportInspector();
1026 #endif
1027     }
1028 }
1029 
ProcessSpringMotion(double position)1030 void Scrollable::ProcessSpringMotion(double position)
1031 {
1032     currentVelocity_ = scrollMotion_->GetCurrentVelocity();
1033     if (scrollMotionFRCSceneCallback_) {
1034         scrollMotionFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
1035     }
1036     if (NearEqual(currentPos_, position)) {
1037         UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION_SPRING);
1038     } else {
1039         moved_ = UpdateScrollPosition(position - currentPos_, SCROLL_FROM_ANIMATION_SPRING);
1040         if (!moved_) {
1041             springController_->Stop();
1042         } else if (!touchUp_) {
1043             if (scrollTouchUpCallback_) {
1044                 scrollTouchUpCallback_();
1045             }
1046             touchUp_ = true;
1047         }
1048     }
1049     currentPos_ = position;
1050 }
1051 
ProcessScrollMotion(double position)1052 void Scrollable::ProcessScrollMotion(double position)
1053 {
1054     if (motion_) {
1055         currentVelocity_ = motion_->GetCurrentVelocity();
1056     }
1057     if (scrollMotionFRCSceneCallback_) {
1058         scrollMotionFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
1059     }
1060     if (snapMotion_ && scrollMotionFRCSceneCallback_) {
1061         scrollMotionFRCSceneCallback_(snapMotion_->GetCurrentVelocity(), NG::SceneStatus::RUNNING);
1062     }
1063     if (needScrollSnapToSideCallback_) {
1064         needScrollSnapChange_ = needScrollSnapToSideCallback_(position - currentPos_);
1065     }
1066     if ((NearEqual(currentPos_, position))) {
1067         UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION);
1068     } else {
1069         // UpdateScrollPosition return false, means reach to scroll limit.
1070         auto mainDelta = position - currentPos_;
1071         HandleScroll(mainDelta, SCROLL_FROM_ANIMATION, NestedState::GESTURE);
1072         if (!moved_) {
1073             controller_->Stop();
1074         } else if (!touchUp_) {
1075             if (scrollTouchUpCallback_) {
1076                 scrollTouchUpCallback_();
1077             }
1078             touchUp_ = true;
1079         }
1080     }
1081     currentPos_ = position;
1082 
1083     // spring effect special process
1084     if ((IsSnapStopped() && canOverScroll_) || needScrollSnapChange_ ||
1085         (!Container::IsCurrentUseNewPipeline() && outBoundaryCallback_ && outBoundaryCallback_())) {
1086         scrollPause_ = true;
1087         controller_->Stop();
1088     }
1089 }
1090 
UpdateScrollPosition(const double offset,int32_t source) const1091 bool Scrollable::UpdateScrollPosition(const double offset, int32_t source) const
1092 {
1093     bool ret = true;
1094     if (callback_) {
1095         ret = callback_(offset, source);
1096     }
1097     return ret;
1098 }
1099 
ProcessScrollOverCallback(double velocity)1100 void Scrollable::ProcessScrollOverCallback(double velocity)
1101 {
1102     if (outBoundaryCallback_ && !outBoundaryCallback_() && !canOverScroll_) {
1103         return;
1104     }
1105     // In the case of chain animation enabled, you need to switch the control point first,
1106     // and then correct the offset value in notification process
1107     if (notifyScrollOverCallback_) {
1108         notifyScrollOverCallback_(velocity);
1109     }
1110     // then use corrected offset to make scroll motion.
1111     if (scrollOverCallback_) {
1112         scrollOverCallback_(velocity);
1113     }
1114 }
1115 
HandleOverScroll(double velocity)1116 bool Scrollable::HandleOverScroll(double velocity)
1117 {
1118     if (!overScrollCallback_) {
1119         if (edgeEffect_ == EdgeEffect::SPRING) {
1120             ProcessScrollOverCallback(velocity);
1121             return true;
1122         }
1123         if (scrollEndCallback_) {
1124             scrollEndCallback_();
1125         }
1126         return false;
1127     }
1128     // call NestableScrollContainer::HandleOverScroll
1129     return overScrollCallback_(velocity);
1130 }
1131 
SetSlipFactor(double SlipFactor)1132 void Scrollable::SetSlipFactor(double SlipFactor)
1133 {
1134     slipFactor_ = std::clamp(SlipFactor, -ADJUSTABLE_VELOCITY, ADJUSTABLE_VELOCITY);
1135 }
1136 
GetDefaultOverSpringProperty()1137 const RefPtr<SpringProperty>& Scrollable::GetDefaultOverSpringProperty()
1138 {
1139     return DEFAULT_OVER_SPRING_PROPERTY;
1140 }
1141 
UpdateScrollSnapEndWithOffset(double offset)1142 void Scrollable::UpdateScrollSnapEndWithOffset(double offset)
1143 {
1144     if (scrollSnapMotion_ && scrollSnapController_ && scrollSnapController_->IsRunning()) {
1145         scrollSnapController_->ClearStopListeners();
1146         scrollSnapController_->Stop();
1147         auto currPos = scrollSnapMotion_->GetCurrentPosition();
1148         auto endPos = scrollSnapMotion_->GetEndValue();
1149         auto velocity = scrollSnapMotion_->GetCurrentVelocity();
1150         scrollSnapMotion_->Reset(currPos, endPos - offset, velocity, DEFAULT_OVER_SPRING_PROPERTY);
1151         scrollSnapController_->PlayMotion(scrollSnapMotion_);
1152         scrollSnapController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
1153             auto scroll = weak.Upgrade();
1154             CHECK_NULL_VOID(scroll);
1155             scroll->ProcessScrollSnapStop();
1156         });
1157     }
1158 }
1159 } // namespace OHOS::Ace
1160