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