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