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