• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2025 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_ng/pattern/scrollable/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/perfmonitor/perf_constants.h"
25 #include "base/perfmonitor/perf_monitor.h"
26 #include "base/ressched/ressched_report.h"
27 #include "base/utils/time_util.h"
28 #include "base/utils/utils.h"
29 #include "core/common/container.h"
30 #include "core/common/layout_inspector.h"
31 #include "core/event/ace_events.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33 
34 namespace OHOS::Ace::NG {
35 namespace {
36 
37 constexpr double CAP_COEFFICIENT = 0.45;
38 constexpr int32_t FIRST_THRESHOLD = 4;
39 constexpr int32_t SECOND_THRESHOLD = 10;
40 constexpr double CAP_FIXED_VALUE = 16.0;
41 constexpr uint32_t DRAG_INTERVAL_TIME = 400;
42 constexpr uint32_t MULTI_FLING_DISTANCE = 125;
43 
44 #ifndef WEARABLE_PRODUCT
45 constexpr double FRICTION = 0.6;
46 constexpr double API11_FRICTION = 0.7;
47 constexpr double API12_FRICTION = 0.75;
48 constexpr double SLOW_FRICTION_THRESHOLD = 3000.0;
49 constexpr double SLOW_FRICTION = 1.0;
50 constexpr double VELOCITY_SCALE = 1.0;
51 constexpr double SLOW_VELOCITY_SCALE = 1.2;
52 constexpr double NEW_VELOCITY_SCALE = 1.5;
53 constexpr double ADJUSTABLE_VELOCITY = 3000.0;
54 #else
55 constexpr double DISTANCE_EPSILON = 1.0;
56 constexpr double FRICTION = 0.9;
57 constexpr double VELOCITY_SCALE = 0.8;
58 constexpr double ADJUSTABLE_VELOCITY = 0.0;
59 #endif
60 constexpr float FRICTION_SCALE = -4.2f;
61 constexpr uint32_t CUSTOM_SPRING_ANIMATION_DURATION = 1000;
62 constexpr uint64_t MILLOS_PER_NANO_SECONDS = 1000 * 1000 * 1000;
63 constexpr uint64_t MIN_DIFF_VSYNC = 1000 * 1000; // min is 1ms
64 constexpr float DEFAULT_THRESHOLD = 0.75f;
65 constexpr float DEFAULT_SPRING_RESPONSE = 0.416f;
66 constexpr float DEFAULT_SPRING_DAMP = 0.99f;
67 constexpr uint32_t MAX_VSYNC_DIFF_TIME = 100 * 1000 * 1000; // max 100 ms
68 constexpr float FRICTION_VELOCITY_THRESHOLD = 120.0f;
69 constexpr float SPRING_ACCURACY = 0.1;
70 #ifdef OHOS_PLATFORM
71 constexpr int64_t INCREASE_CPU_TIME_ONCE = 4000000000; // 4s(unit: ns)
72 #endif
73 
74 } // namespace
75 
76 // Static Functions.
77 std::optional<double> Scrollable::sFriction_ = std::nullopt;
78 std::optional<double> Scrollable::sVelocityScale_ = std::nullopt;
79 
SetVelocityScale(double sVelocityScale)80 void Scrollable::SetVelocityScale(double sVelocityScale)
81 {
82     if (LessOrEqual(sVelocityScale, 0.0)) {
83         return;
84     }
85     sVelocityScale_ = sVelocityScale;
86 }
87 
GetVelocityScale()88 double Scrollable::GetVelocityScale()
89 {
90     return Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ?
91         NEW_VELOCITY_SCALE : VELOCITY_SCALE;
92 }
93 
SetFriction(double sFriction)94 void Scrollable::SetFriction(double sFriction)
95 {
96     if (LessOrEqual(sFriction, 0.0)) {
97         return;
98     }
99     sFriction_ = sFriction;
100 }
101 
Scrollable()102 Scrollable::Scrollable()
103 {
104     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
105     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
106     velocityScale_ =
107         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_VELOCITY_SCALE : VELOCITY_SCALE;
108 }
109 
Scrollable(ScrollPositionCallback && callback,Axis axis)110 Scrollable::Scrollable(ScrollPositionCallback&& callback, Axis axis) : callback_(std::move(callback)), axis_(axis)
111 {
112     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
113     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
114     velocityScale_ =
115         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_VELOCITY_SCALE : VELOCITY_SCALE;
116 }
117 
Scrollable(const ScrollPositionCallback & callback,Axis axis)118 Scrollable::Scrollable(const ScrollPositionCallback& callback, Axis axis) : callback_(callback), axis_(axis)
119 {
120     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
121     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
122     velocityScale_ =
123         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_VELOCITY_SCALE : VELOCITY_SCALE;
124 }
125 
~Scrollable()126 Scrollable::~Scrollable()
127 {
128     // If animation still runs, force stop it.
129     if (!IsStopped()) {
130         PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
131         AceAsyncTraceEnd(0, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") + nodeTag_).c_str());
132     }
133     StopFrictionAnimation();
134     StopSpringAnimation();
135     StopSnapAnimation();
136 }
137 
Initialize(PipelineContext * context)138 void Scrollable::Initialize(PipelineContext* context)
139 {
140     auto weakContext = WeakClaim(context);
141     Initialize(weakContext);
142 }
143 
Initialize(const WeakPtr<PipelineBase> & context)144 void Scrollable::Initialize(const WeakPtr<PipelineBase>& context)
145 {
146     context_ = context;
147     PanDirection panDirection;
148     if (axis_ == Axis::VERTICAL) {
149         panDirection.type = PanDirection::VERTICAL;
150     } else {
151         panDirection.type = PanDirection::HORIZONTAL;
152     }
153 
154     auto actionStart = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
155         auto scroll = weakScroll.Upgrade();
156         if (scroll) {
157             scroll->isDragging_ = true;
158             scroll->HandleDragStart(info);
159         }
160     };
161 
162     auto actionUpdate = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
163         auto scroll = weakScroll.Upgrade();
164         if (scroll) {
165             scroll->HandleDragUpdate(info);
166         }
167     };
168 
169     auto actionEnd = [weakScroll = AceType::WeakClaim(this)](GestureEvent& info) {
170         auto scroll = weakScroll.Upgrade();
171         if (scroll) {
172             scroll->HandleDragEnd(info);
173             if (scroll->panActionEndEvents_.empty()) {
174                 scroll->isDragging_ = false;
175                 return;
176             }
177             std::for_each(scroll->panActionEndEvents_.begin(), scroll->panActionEndEvents_.end(),
178                 [info](GestureEventFunc& event) {
179                     auto gestureInfo = info;
180                     event(gestureInfo);
181                 });
182             scroll->isDragging_ = false;
183         }
184     };
185 
186     auto actionCancel = [weakScroll = AceType::WeakClaim(this)]() {
187         auto scroll = weakScroll.Upgrade();
188         if (!scroll) {
189             return;
190         }
191         if (scroll->dragCancelCallback_) {
192             scroll->dragCancelCallback_();
193         }
194         GestureEvent info;
195         scroll->HandleDragEnd(info);
196         if (scroll->panActionEndEvents_.empty()) {
197             scroll->isDragging_ = false;
198             return;
199         }
200         std::for_each(scroll->panActionEndEvents_.begin(), scroll->panActionEndEvents_.end(),
201             [info](GestureEventFunc& event) {
202                 auto gestureInfo = info;
203                 event(gestureInfo);
204             });
205         scroll->isDragging_ = false;
206     };
207 
208     if (Container::IsCurrentUseNewPipeline()) {
209         panRecognizerNG_ = AceType::MakeRefPtr<NG::PanRecognizer>(
210             DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
211         panRecognizerNG_->SetIsAllowMouse(false);
212         panRecognizerNG_->SetOnActionStart(actionStart);
213         panRecognizerNG_->SetOnActionUpdate(actionUpdate);
214         panRecognizerNG_->SetOnActionEnd(actionEnd);
215         panRecognizerNG_->SetOnActionCancel(actionCancel);
216     }
217     available_ = true;
218 }
219 
SetAxis(Axis axis)220 void Scrollable::SetAxis(Axis axis)
221 {
222     axis_ = axis;
223     PanDirection panDirection;
224     if (axis_ == Axis::NONE) {
225         panDirection.type = PanDirection::NONE;
226     } else if (axis_ == Axis::VERTICAL) {
227         panDirection.type = PanDirection::VERTICAL;
228     } else {
229         panDirection.type = PanDirection::HORIZONTAL;
230     }
231     if (panRecognizerNG_) {
232         panRecognizerNG_->SetDirection(panDirection);
233     }
234 }
235 
HandleTouchDown()236 void Scrollable::HandleTouchDown()
237 {
238     isTouching_ = true;
239     // If animation still runs, first stop it.
240     ACE_SCOPED_TRACE("HandleTouchDown, panDirection:%u, id:%d, tag:%s", GetPanDirection(), nodeId_, nodeTag_.c_str());
241     StopSpringAnimation();
242     if (!isFrictionAnimationStop_) {
243         StopFrictionAnimation();
244     } else if (!isSnapAnimationStop_ || !isSnapScrollAnimationStop_) {
245         StopSnapAnimation();
246     } else {
247         // Resets values.
248         currentPos_ = 0.0;
249     }
250 }
251 
HandleTouchUp()252 void Scrollable::HandleTouchUp()
253 {
254     // Two fingers are alternately drag, one finger is released without triggering spring animation.
255     ACE_SCOPED_TRACE("HandleTouchUp, isDragging_:%u, nestedScrolling_:%u id:%d, tag:%s",
256         isDragging_, nestedScrolling_, nodeId_, nodeTag_.c_str());
257     if (isDragging_) {
258         return;
259     }
260     isTouching_ = false;
261     if (nestedScrolling_) {
262         return;
263     }
264     // outBoundaryCallback_ is only set in ScrollablePattern::SetEdgeEffect and when the edge effect is spring
265     if (outBoundaryCallback_ && outBoundaryCallback_()) {
266         if (isSpringAnimationStop_ && scrollOverCallback_) {
267             if (onScrollStartRec_) {
268                 onScrollStartRec_(static_cast<float>(axis_));
269             }
270             ProcessScrollOverCallback(0.0);
271         }
272         return;
273     }
274     if (isSnapScrollAnimationStop_ && scrollSnapCallback_) {
275         scrollSnapCallback_(0.0, 0.0);
276     }
277 }
278 
HandleTouchCancel()279 void Scrollable::HandleTouchCancel()
280 {
281     isTouching_ = false;
282     ACE_SCOPED_TRACE("HandleTouchCancel, id:%d, tag:%s", nodeId_, nodeTag_.c_str());
283     if (isSpringAnimationStop_ && scrollOverCallback_) {
284         ProcessScrollOverCallback(0.0);
285     }
286 }
287 
IsAnimationNotRunning() const288 bool Scrollable::IsAnimationNotRunning() const
289 {
290     return !isTouching_ && isFrictionAnimationStop_ && isSpringAnimationStop_
291         && isSnapAnimationStop_ && isSnapScrollAnimationStop_;
292 }
293 
Idle() const294 bool Scrollable::Idle() const
295 {
296     return !isTouching_ && isFrictionAnimationStop_ && isSpringAnimationStop_
297         && isSnapAnimationStop_ && isSnapScrollAnimationStop_ && !nestedScrolling_;
298 }
299 
IsStopped() const300 bool Scrollable::IsStopped() const
301 {
302     return isSpringAnimationStop_ && isFrictionAnimationStop_
303         && isSnapAnimationStop_ && isSnapScrollAnimationStop_;
304 }
305 
IsSpringStopped() const306 bool Scrollable::IsSpringStopped() const
307 {
308     return isSpringAnimationStop_;
309 }
310 
IsSnapStopped() const311 bool Scrollable::IsSnapStopped() const
312 {
313     return isSnapAnimationStop_;
314 }
315 
StopScrollable()316 void Scrollable::StopScrollable()
317 {
318     if (!isFrictionAnimationStop_) {
319         StopFrictionAnimation();
320     }
321     if (!isSpringAnimationStop_) {
322         StopSpringAnimation();
323     }
324     if (!isSnapAnimationStop_ || !isSnapScrollAnimationStop_) {
325         StopSnapAnimation();
326     }
327 }
328 
HandleScrollEnd(const std::optional<float> & velocity)329 void Scrollable::HandleScrollEnd(const std::optional<float>& velocity)
330 {
331     // priority:
332     //  1. onScrollEndRec_ (would internally call onScrollEnd)
333     //  2. scrollEndCallback_
334     if (onScrollEndRec_) {
335         onScrollEndRec_(velocity);
336         return;
337     }
338     if (scrollEndCallback_) {
339         scrollEndCallback_();
340     }
341 }
342 
HandleDragStart(const OHOS::Ace::GestureEvent & info)343 void Scrollable::HandleDragStart(const OHOS::Ace::GestureEvent& info)
344 {
345     ACE_SCOPED_TRACE("HandleDragStart, id:%d, tag:%s", nodeId_, nodeTag_.c_str());
346     if (info.GetSourceTool() == SourceTool::TOUCHPAD) {
347         HandleTouchDown();
348     }
349     currentVelocity_ = info.GetMainVelocity();
350     if (dragFRCSceneCallback_) {
351         dragFRCSceneCallback_(currentVelocity_, NG::SceneStatus::START);
352     }
353     if (continuousDragStatus_) {
354         IncreaseContinueDragCount();
355         task_.Cancel();
356     }
357     SetDragStartPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
358     const double dragPositionInMainAxis =
359         axis_ == Axis::VERTICAL ? info.GetGlobalLocation().GetY() : info.GetGlobalLocation().GetX();
360     TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Scroll drag start, id:%{public}d, tag:%{public}s", nodeId_, nodeTag_.c_str());
361 
362     skipRestartSpring_ = false; // reset flags. Extract method if more flags need to be reset
363 
364 #ifdef OHOS_PLATFORM
365     // Increase the cpu frequency when sliding start.
366     auto currentTime = GetSysTimestamp();
367     auto increaseCpuTime = currentTime - startIncreaseTime_;
368     if (!moved_ || increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
369         startIncreaseTime_ = currentTime;
370         if (FrameReport::GetInstance().GetEnable()) {
371             FrameReport::GetInstance().BeginListFling();
372         }
373     }
374 #endif
375     JankFrameReport::GetInstance().SetFrameJankFlag(JANK_RUNNING_SCROLL);
376     if (onScrollStartRec_) {
377         onScrollStartRec_(static_cast<float>(dragPositionInMainAxis));
378     }
379 }
380 
HandleScroll(double offset,int32_t source,NestedState state)381 ScrollResult Scrollable::HandleScroll(double offset, int32_t source, NestedState state)
382 {
383     if (!handleScrollCallback_) {
384         ExecuteScrollBegin(offset);
385         canOverScroll_ = false;
386         moved_ = UpdateScrollPosition(offset, source);
387         return { 0, false };
388     }
389     // call NestableScrollContainer::HandleScroll
390     return handleScrollCallback_(static_cast<float>(offset), source, state);
391 }
392 
HandleDragUpdate(const GestureEvent & info)393 void Scrollable::HandleDragUpdate(const GestureEvent& info)
394 {
395     currentVelocity_ = info.GetMainVelocity();
396     if (dragFRCSceneCallback_) {
397         dragFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
398     }
399     if (!NearZero(info.GetMainVelocity()) && dragCount_ >= FIRST_THRESHOLD) {
400         if (Negative(lastVelocity_ / info.GetMainVelocity())) {
401             ResetContinueDragCount();
402         }
403     }
404     if (!isSpringAnimationStop_ || !isFrictionAnimationStop_ ||
405         !isSnapAnimationStop_ || !isSnapScrollAnimationStop_) {
406         // If animation still runs, first stop it.
407         isDragUpdateStop_ = true;
408         StopFrictionAnimation();
409         StopSpringAnimation();
410         StopSnapAnimation();
411         currentPos_ = 0.0;
412     }
413 #ifdef OHOS_PLATFORM
414     // Handle the case where you keep sliding past limit time(4s).
415     auto currentTime = GetSysTimestamp();
416     auto increaseCpuTime = currentTime - startIncreaseTime_;
417     if (increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
418         startIncreaseTime_ = currentTime;
419         if (FrameReport::GetInstance().GetEnable()) {
420             FrameReport::GetInstance().BeginListFling();
421         }
422     }
423 #endif
424     auto mainDelta = info.GetMainDelta();
425     lastMainDelta_ = mainDelta;
426     auto source = IsMouseWheelScroll(info) ? SCROLL_FROM_AXIS : SCROLL_FROM_UPDATE;
427     ACE_SCOPED_TRACE(
428         "HandleDragUpdate, mainDelta:%f, source:%d, id:%d, tag:%s", mainDelta, source, nodeId_, nodeTag_.c_str());
429     if (isReverseCallback_ && isReverseCallback_()) {
430         mainDelta = Round(-mainDelta);
431     } else {
432         mainDelta = Round(mainDelta);
433     }
434     JankFrameReport::GetInstance().RecordFrameUpdate();
435     HandleScroll(mainDelta, source, NestedState::GESTURE);
436 }
437 
LayoutDirectionEst(double & correctVelocity)438 void Scrollable::LayoutDirectionEst(double& correctVelocity)
439 {
440     auto defaultVelocityScale = isSlow_ ? SLOW_VELOCITY_SCALE : velocityScale_;
441     if (isReverseCallback_ && isReverseCallback_()) {
442         correctVelocity = -correctVelocity * sVelocityScale_.value_or(defaultVelocityScale) * GetGain(GetDragOffset());
443     } else {
444         correctVelocity = correctVelocity * sVelocityScale_.value_or(defaultVelocityScale) * GetGain(GetDragOffset());
445     }
446 }
447 
HandleDragEnd(const GestureEvent & info)448 void Scrollable::HandleDragEnd(const GestureEvent& info)
449 {
450     // avoid no render frame when drag end
451     if (NearZero(info.GetMainDelta())) {
452         auto tempInfo = info;
453         tempInfo.SetMainDelta(lastMainDelta_);
454         HandleDragUpdate(tempInfo);
455     } else {
456         HandleDragUpdate(info);
457     }
458     TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Scroll drag end, velocity is %{public}f id:%{public}d, tag:%{public}s",
459         info.GetMainVelocity(), nodeId_, nodeTag_.c_str());
460     if (dragFRCSceneCallback_) {
461         dragFRCSceneCallback_(info.GetMainVelocity(), NG::SceneStatus::END);
462     }
463     isDragUpdateStop_ = false;
464     touchUp_ = false;
465     scrollPause_ = false;
466     lastVelocity_ = GetPanDirection() == Axis::NONE ? 0.0 : info.GetMainVelocity();
467     double gestureVelocity = GetPanDirection() == Axis::NONE ? 0.0 : info.GetMainVelocity();
468     isSlow_ = LessNotEqual(std::abs(gestureVelocity), SLOW_FRICTION_THRESHOLD);
469     SetDragEndPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
470     LayoutDirectionEst(gestureVelocity);
471     // Apply max fling velocity limit, it must be calculated after all fling velocity gain.
472     currentVelocity_ = std::clamp(gestureVelocity, -maxFlingVelocity_ + slipFactor_, maxFlingVelocity_ - slipFactor_);
473 
474     lastPos_ = GetDragOffset();
475     JankFrameReport::GetInstance().ClearFrameJankFlag(JANK_RUNNING_SCROLL);
476     double mainPosition = GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY()));
477     mainPosition = Round(mainPosition);
478     ACE_SCOPED_TRACE(
479         "HandleDragEnd, mainPosition:%f, getureDelta:%lf, gestureVelocity:%lf, currentVelocity:%f, moved_:%u "
480         "canOverScroll_:%u, id:%d, tag:%s",
481         mainPosition, info.GetMainDelta(), gestureVelocity, currentVelocity_, moved_, canOverScroll_, nodeId_,
482         nodeTag_.c_str());
483     if (!moved_ || IsMouseWheelScroll(info)) {
484         ResetContinueDragCount();
485         if (calePredictSnapOffsetCallback_) {
486             std::optional<float> predictSnapOffset = calePredictSnapOffsetCallback_(0.0f, 0.0f, 0.0f);
487             if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
488                 currentPos_ = mainPosition;
489                 ProcessScrollSnapSpringMotion(predictSnapOffset.value(), currentVelocity_);
490                 isTouching_ = false;
491                 return;
492             }
493         }
494         HandleScrollEnd(currentVelocity_);
495         currentVelocity_ = 0.0;
496 #ifdef OHOS_PLATFORM
497         if (FrameReport::GetInstance().GetEnable()) {
498             FrameReport::GetInstance().EndListFling();
499         }
500 #endif
501     } else if (!Container::IsCurrentUseNewPipeline() && outBoundaryCallback_ && outBoundaryCallback_() &&
502                scrollOverCallback_) {
503         ResetContinueDragCount();
504         ProcessScrollOverCallback(currentVelocity_);
505     } else if (canOverScroll_) {
506         ResetContinueDragCount();
507         HandleOverScroll(currentVelocity_);
508     } else {
509         StartScrollAnimation(mainPosition, currentVelocity_);
510     }
511     SetDelayedTask();
512     if (dragEndCallback_) {
513         dragEndCallback_();
514     }
515     isSlow_ = false;
516     isTouching_ = false;
517 }
518 
StartScrollAnimation(float mainPosition,float correctVelocity)519 void Scrollable::StartScrollAnimation(float mainPosition, float correctVelocity)
520 {
521     if (!isSpringAnimationStop_) {
522         StopSpringAnimation();
523     }
524     if (!frictionOffsetProperty_) {
525         GetFrictionProperty();
526     }
527     StopSnapController();
528     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "The position of scroll motion is %{public}f, velocity is %{public}f",
529         mainPosition, correctVelocity);
530     auto defaultFriction = isSlow_ ? SLOW_FRICTION : friction_;
531     float friction = sFriction_.value_or(defaultFriction);
532     initVelocity_ = correctVelocity;
533     finalPosition_ = mainPosition + correctVelocity / (friction * -FRICTION_SCALE);
534 
535     if (calePredictSnapOffsetCallback_) {
536         std::optional<float> predictSnapOffset =
537           calePredictSnapOffsetCallback_(GetFinalPosition() - mainPosition, GetDragOffset(), correctVelocity);
538         if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
539             currentPos_ = mainPosition;
540             ProcessScrollSnapSpringMotion(predictSnapOffset.value(), correctVelocity);
541             return;
542         }
543     }
544     if (scrollSnapCallback_ && scrollSnapCallback_(GetFinalPosition() - mainPosition, correctVelocity)) {
545         currentVelocity_ = 0.0;
546         return;
547     }
548     if (NearZero(correctVelocity, FRICTION_VELOCITY_THRESHOLD)) {
549         HandleScrollEnd(correctVelocity);
550         currentVelocity_ = 0.0;
551 #ifdef OHOS_PLATFORM
552         if (FrameReport::GetInstance().GetEnable()) {
553             FrameReport::GetInstance().EndListFling();
554         }
555 #endif
556         return;
557     }
558     // change motion param when list item need to be center of screen on watch
559     FixScrollMotion(mainPosition, correctVelocity);
560     // Resets values.
561     currentPos_ = mainPosition;
562     currentVelocity_ = 0.0;
563     lastPosition_ = currentPos_;
564     frictionVelocity_ = initVelocity_;
565     frictionOffsetProperty_->Set(mainPosition);
566     float response = fabs(2 * M_PI / (FRICTION_SCALE * friction));
567     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(response, 1.0f, 0.0f);
568     AnimationOption option;
569     option.SetCurve(curve);
570     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
571     option.SetFinishCallbackType(FinishCallbackType::LOGICALLY);
572     frictionOffsetProperty_->SetThresholdType(ThresholdType::LAYOUT);
573     frictionOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
574     ACE_SCOPED_TRACE("Scrollable friction animation start, start:%f, end:%f, vel:%f, id:%d, tag:%s", mainPosition,
575         finalPosition_, initVelocity_, nodeId_, nodeTag_.c_str());
576     frictionOffsetProperty_->AnimateWithVelocity(
577         option, finalPosition_, initVelocity_, [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
578             ContainerScope scope(id);
579             auto scroll = weak.Upgrade();
580             CHECK_NULL_VOID(scroll);
581             scroll->isFrictionAnimationStop_ = true;
582             ACE_SCOPED_TRACE(
583                 "Scrollable friction animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
584             scroll->ProcessScrollMotionStop(true);
585         });
586     isFrictionAnimationStop_ = false;
587     auto context = context_.Upgrade();
588     CHECK_NULL_VOID(context);
589     context->RequestFrame();
590     lastVsyncTime_ = context->GetVsyncTime();
591 }
592 
SetDelayedTask()593 void Scrollable::SetDelayedTask()
594 {
595     SetContinuousDragStatus(true);
596     auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
597     CHECK_NULL_VOID(context);
598     auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
599     task_.Reset([weak = WeakClaim(this)] {
600         auto drag = weak.Upgrade();
601         if (drag) {
602             drag->ResetContinueDragCount();
603             drag->SetContinuousDragStatus(false);
604         }
605     });
606     taskExecutor.PostDelayedTask(task_, DRAG_INTERVAL_TIME, "ArkUIScrollDragInterval");
607 }
608 
MarkNeedFlushAnimationStartTime()609 void Scrollable::MarkNeedFlushAnimationStartTime()
610 {
611     auto context = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
612     CHECK_NULL_VOID(context);
613     context->MarkNeedFlushAnimationStartTime();
614 }
615 
ComputeCap(int dragCount)616 double Scrollable::ComputeCap(int dragCount)
617 {
618     if (dragCount < FIRST_THRESHOLD) {
619         return 1.0;
620     }
621     auto cap = ComputeCap(dragCount - 1) + CAP_COEFFICIENT * (dragCount - 1);
622     return cap;
623 }
624 
GetGain(double delta)625 double Scrollable::GetGain(double delta)
626 {
627     auto cap = 1.0;
628     auto gain = 1.0;
629     if (std::abs(delta) < MULTI_FLING_DISTANCE) {
630         ResetContinueDragCount();
631         preGain_ = gain;
632         return gain;
633     }
634     if (!continuousSlidingCallback_) {
635         preGain_ = gain;
636         return gain;
637     }
638     auto screenHeight = continuousSlidingCallback_();
639     if (delta == 0 || screenHeight == 0) {
640         preGain_ = gain;
641         return gain;
642     }
643     if (dragCount_ >= FIRST_THRESHOLD && dragCount_ < SECOND_THRESHOLD) {
644         if (Negative(lastPos_ / delta)) {
645             ResetContinueDragCount();
646             preGain_ = gain;
647             return gain;
648         }
649         cap = CAP_COEFFICIENT * (dragCount_ - 1);
650         gain = (LessNotEqual(cap, std::abs(delta) / screenHeight * (dragCount_ - 1))) ? preGain_ + cap :
651             preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1);
652     } else if (dragCount_ >= SECOND_THRESHOLD) {
653         if (Negative(lastPos_ / delta)) {
654             ResetContinueDragCount();
655             preGain_ = gain;
656             return gain;
657         }
658         cap = CAP_FIXED_VALUE;
659         gain = (LessNotEqual(cap, preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1))) ? cap :
660             preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1);
661     }
662     preGain_ = gain;
663     return gain;
664 }
665 
ExecuteScrollBegin(double & mainDelta)666 void Scrollable::ExecuteScrollBegin(double& mainDelta)
667 {
668     auto context = context_.Upgrade();
669     if (!scrollBeginCallback_ || !context) {
670         return;
671     }
672 
673     ScrollInfo scrollInfo;
674     if (axis_ == Axis::VERTICAL) {
675         scrollInfo = scrollBeginCallback_(0.0_vp, Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP));
676         mainDelta = context->NormalizeToPx(scrollInfo.dy);
677     } else if (axis_ == Axis::HORIZONTAL) {
678         scrollInfo = scrollBeginCallback_(Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP), 0.0_vp);
679         mainDelta = context->NormalizeToPx(scrollInfo.dx);
680     }
681 }
682 
GetFrictionVelocityByFinalPosition(float final,float position,float friction,float signum,float threshold)683 float Scrollable::GetFrictionVelocityByFinalPosition(float final, float position, float friction,
684     float signum, float threshold)
685 {
686     return DEFAULT_THRESHOLD * threshold * signum - (final - position) * friction;
687 }
688 
FixScrollMotion(float position,float initVelocity)689 void Scrollable::FixScrollMotion(float position, float initVelocity)
690 {
691 #ifdef WEARABLE_PRODUCT
692     float signum = 0.0;
693     if (!NearZero(initVelocity)) {
694         signum = GreatNotEqual(initVelocity, 0.0) ? 1.0 : -1.0;
695     }
696     if (frictionOffsetProperty_ && needCenterFix_ && watchFixCallback_) {
697         float finalPosition = watchFixCallback_(GetFinalPosition(), position);
698         if (!NearEqual(finalPosition, GetFinalPosition(), DISTANCE_EPSILON)) {
699             float friction = sFriction_.value_or(friction_);
700             float velocity = GetFrictionVelocityByFinalPosition(finalPosition, position, friction, signum);
701 
702             // fix again when velocity is less than velocity threshold
703             if (!NearEqual(finalPosition, GetFinalPosition(), DISTANCE_EPSILON)) {
704                 velocity = GetFrictionVelocityByFinalPosition(finalPosition, position, friction, signum, 0.0f);
705             }
706             initVelocity_ = velocity;
707             finalPosition_ = mainPosition + initVelocity_ / (friction * -FRICTION_SCALE);
708         }
709     }
710 #endif
711 }
712 
StartScrollSnapMotion(float predictSnapOffset,float scrollSnapVelocity)713 void Scrollable::StartScrollSnapMotion(float predictSnapOffset, float scrollSnapVelocity)
714 {
715     endPos_ = currentPos_ + predictSnapOffset;
716     AnimationOption option;
717     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
718     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
719     option.SetCurve(curve);
720     if (!snapOffsetProperty_) {
721         GetSnapProperty();
722     }
723     snapOffsetProperty_->Set(currentPos_);
724     snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
725     ACE_SCOPED_TRACE("List snap animation start, start:%f, end:%f, vel:%f, id:%d", currentPos_, endPos_,
726         scrollSnapVelocity, nodeId_);
727     updateSnapAnimationCount_++;
728     snapOffsetProperty_->AnimateWithVelocity(option, endPos_, scrollSnapVelocity,
729         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
730             ContainerScope scope(id);
731             auto scroll = weak.Upgrade();
732             CHECK_NULL_VOID(scroll);
733             ACE_SCOPED_TRACE("List snap animation finish, id:%d", scroll->nodeId_);
734             scroll->updateSnapAnimationCount_--;
735             if (scroll->updateSnapAnimationCount_ == 0) {
736                 scroll->isSnapScrollAnimationStop_ = true;
737                 scroll->ProcessScrollSnapStop();
738             }
739     });
740     isSnapScrollAnimationStop_ = false;
741     auto context = context_.Upgrade();
742     CHECK_NULL_VOID(context);
743     lastVsyncTime_ = context->GetVsyncTime();
744     MarkNeedFlushAnimationStartTime();
745 }
746 
ProcessScrollSnapSpringMotion(float scrollSnapDelta,float scrollSnapVelocity)747 void Scrollable::ProcessScrollSnapSpringMotion(float scrollSnapDelta, float scrollSnapVelocity)
748 {
749     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "The snap delta of scroll motion is %{public}f, "
750         "The snap velocity of scroll motion is %{public}f",
751         scrollSnapDelta, scrollSnapVelocity);
752     endPos_ = currentPos_ + scrollSnapDelta;
753     ACE_SCOPED_TRACE("Scroll snap animation start, start:%f, end:%f, vel:%f, id:%d", currentPos_, endPos_,
754         scrollSnapVelocity, nodeId_);
755     AnimationOption option;
756     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
757     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
758     option.SetCurve(curve);
759     if (!snapOffsetProperty_) {
760         GetSnapProperty();
761     }
762     snapOffsetProperty_->Set(currentPos_);
763     snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
764     snapOffsetProperty_->AnimateWithVelocity(option, endPos_, scrollSnapVelocity,
765         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
766             ContainerScope scope(id);
767             auto scroll = weak.Upgrade();
768             CHECK_NULL_VOID(scroll);
769             scroll->isSnapAnimationStop_ = true;
770             ACE_SCOPED_TRACE("Scroll snap animation finish, id:%d", scroll->nodeId_);
771             scroll->ProcessScrollMotionStop(false);
772     });
773     isSnapAnimationStop_ = false;
774     auto context = context_.Upgrade();
775     CHECK_NULL_VOID(context);
776     lastVsyncTime_ = context->GetVsyncTime();
777 }
778 
UpdateScrollSnapStartOffset(double offset)779 void Scrollable::UpdateScrollSnapStartOffset(double offset)
780 {
781     UpdateScrollSnapEndWithOffset(offset);
782 }
783 
ProcessScrollSnapMotion(double position)784 void Scrollable::ProcessScrollSnapMotion(double position)
785 {
786     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "Current Pos is %{public}f, position is %{public}f",
787         currentPos_, position);
788     currentVelocity_ = snapVelocity_;
789     if (NearEqual(currentPos_, position)) {
790         UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION);
791     } else {
792         auto mainDelta = position - currentPos_;
793         HandleScroll(mainDelta, SCROLL_FROM_ANIMATION, NestedState::GESTURE);
794         if (!moved_ && !isSnapScrollAnimationStop_) {
795             StopSnapAnimation();
796         } else if (!touchUp_) {
797             if (scrollTouchUpCallback_) {
798                 scrollTouchUpCallback_();
799             }
800             touchUp_ = true;
801         }
802     }
803     currentPos_ = position;
804     if (outBoundaryCallback_ && outBoundaryCallback_()  && !isSnapScrollAnimationStop_) {
805         scrollPause_ = true;
806         skipRestartSpring_ = true;
807         MarkNeedFlushAnimationStartTime();
808         StopSnapAnimation();
809     }
810 }
811 
ProcessScrollSnapStop()812 void Scrollable::ProcessScrollSnapStop()
813 {
814     if (scrollPause_) {
815         scrollPause_ = false;
816         HandleOverScroll(currentVelocity_);
817     } else {
818         OnAnimateStop();
819     }
820 }
821 
OnAnimateStop()822 void Scrollable::OnAnimateStop()
823 {
824     HandleScrollEnd(std::nullopt);
825     currentVelocity_ = 0.0;
826     if (isTouching_ || isDragUpdateStop_) {
827         return;
828     }
829     moved_ = false;
830 #ifdef OHOS_PLATFORM
831     if (FrameReport::GetInstance().GetEnable()) {
832         FrameReport::GetInstance().EndListFling();
833     }
834 #endif
835     if (scrollEnd_) {
836         scrollEnd_();
837     }
838 #if !defined(PREVIEW)
839     LayoutInspector::SupportInspector();
840 #endif
841 }
842 
StartSpringMotion(double mainPosition,double mainVelocity,const ExtentPair & extent,const ExtentPair & initExtent)843 void Scrollable::StartSpringMotion(
844     double mainPosition, double mainVelocity, const ExtentPair& extent, const ExtentPair& initExtent)
845 {
846     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "position is %{public}f, mainVelocity is %{public}f, minExtent is "
847         "%{public}f, maxExtent is %{public}f, initMinExtent is %{public}f, initMaxExtent is %{public}f",
848         mainPosition, mainVelocity, extent.Leading(), extent.Trailing(), initExtent.Leading(), initExtent.Trailing());
849     if (!isSpringAnimationStop_ || (skipRestartSpring_ && NearEqual(mainVelocity, 0.0f, 0.001f))) {
850         return;
851     }
852     currentPos_ = mainPosition;
853     if (mainPosition > initExtent.Trailing() || NearEqual(mainPosition, initExtent.Trailing(), 0.01f)) {
854         finalPosition_ = extent.Trailing();
855     } else if (mainPosition <  initExtent.Leading() || NearEqual(mainPosition, initExtent.Leading(), 0.01f)) {
856         finalPosition_ = extent.Leading();
857     } else {
858         return;
859     }
860 
861     if (!springOffsetProperty_) {
862         GetSpringProperty();
863     }
864     springAnimationCount_++;
865     springOffsetProperty_->Set(mainPosition);
866     AnimationOption option;
867     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
868     option.SetCurve(curve);
869     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
870     springOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
871     ACE_SCOPED_TRACE("Scrollable spring animation start, start:%f, end:%f, vel:%f, id:%d, tag:%s", mainPosition,
872         finalPosition_, mainVelocity, nodeId_, nodeTag_.c_str());
873     lastVsyncTime_ = static_cast<uint64_t>(GetSysTimestamp());
874     springOffsetProperty_->AnimateWithVelocity(
875         option, finalPosition_, mainVelocity, [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
876             ContainerScope scope(id);
877             auto scroll = weak.Upgrade();
878             CHECK_NULL_VOID(scroll);
879             scroll->springAnimationCount_--;
880             ACE_SCOPED_TRACE(
881                 "Scrollable spring animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
882             // avoid current animation being interrupted by the prev animation's finish callback
883             // and triggering onScrollStop when spring animation turns to friction animation.
884             if (scroll->springAnimationCount_ > 0 || scroll->scrollPause_) {
885                 return;
886             }
887             scroll->isSpringAnimationStop_ = true;
888             scroll->currentVelocity_ = 0.0;
889             scroll->OnAnimateStop();
890         });
891     isSpringAnimationStop_ = false;
892     skipRestartSpring_ = false;
893     auto context = context_.Upgrade();
894     CHECK_NULL_VOID(context);
895     context->RequestFrame();
896 }
897 
UpdateSpringMotion(double mainPosition,const ExtentPair & extent,const ExtentPair & initExtent)898 void Scrollable::UpdateSpringMotion(
899     double mainPosition, const ExtentPair& extent, const ExtentPair& initExtent)
900 {
901     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "position is %{public}f, minExtent is "
902         "%{public}f, maxExtent is %{public}f, initMinExtent is %{public}f, initMaxExtent is %{public}f",
903         mainPosition, extent.Leading(), extent.Trailing(), initExtent.Leading(), initExtent.Trailing());
904     if (isSpringAnimationStop_) {
905         return;
906     }
907     float finalPosition = 0.0f;
908     if (mainPosition > initExtent.Trailing() || NearEqual(mainPosition, initExtent.Trailing())) {
909         finalPosition = extent.Trailing();
910     } else if (mainPosition <  initExtent.Leading() || NearEqual(mainPosition, initExtent.Leading())) {
911         finalPosition = extent.Leading();
912     } else {
913         return;
914     }
915 
916     finalPosition = finalPosition_ + (finalPosition - mainPosition) - (finalPosition_ - currentPos_);
917     if (NearEqual(finalPosition, finalPosition_, SPRING_ACCURACY)) {
918         return;
919     }
920     finalPosition_ = finalPosition;
921     springAnimationCount_++;
922     AnimationOption option;
923     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
924     option.SetCurve(curve);
925     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
926     springOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
927     ACE_SCOPED_TRACE("Scrollable spring animation update, start:%f, end:%f, id:%d, tag:%s", mainPosition,
928         finalPosition_, nodeId_, nodeTag_.c_str());
929     AnimationUtils::StartAnimation(
930         option,
931         [weak = AceType::WeakClaim(this)]() {
932             auto scroll = weak.Upgrade();
933             CHECK_NULL_VOID(scroll);
934             scroll->springOffsetProperty_->Set(scroll->finalPosition_);
935             scroll->isSpringAnimationStop_ = false;
936         },
937         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
938             ContainerScope scope(id);
939             auto scroll = weak.Upgrade();
940             CHECK_NULL_VOID(scroll);
941             scroll->springAnimationCount_--;
942             // avoid current animation being interrupted by the prev animation's finish callback
943             if (scroll->springAnimationCount_ > 0) {
944                 return;
945             }
946             ACE_SCOPED_TRACE(
947                 "Scrollable updated spring animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
948             scroll->isSpringAnimationStop_ = true;
949             scroll->currentVelocity_ = 0.0;
950             scroll->OnAnimateStop();
951     });
952     isSpringAnimationStop_ = false;
953     skipRestartSpring_ = false;
954 }
955 
ProcessScrollMotionStop(bool stopFriction)956 void Scrollable::ProcessScrollMotionStop(bool stopFriction)
957 {
958     if (needScrollSnapChange_ && calePredictSnapOffsetCallback_ && frictionOffsetProperty_) {
959         needScrollSnapChange_ = false;
960         auto predictSnapOffset = calePredictSnapOffsetCallback_(GetFinalPosition() - currentPos_, 0.0f, 0.0f);
961         if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
962             ProcessScrollSnapSpringMotion(predictSnapOffset.value(), currentVelocity_);
963             return;
964         }
965     }
966     // spring effect special process
967     if (scrollPause_) {
968         scrollPause_ = false;
969         HandleOverScroll(currentVelocity_);
970     } else {
971         if (isDragUpdateStop_) {
972             return;
973         }
974         moved_ = false;
975         HandleScrollEnd(std::nullopt);
976 #ifdef OHOS_PLATFORM
977         if (FrameReport::GetInstance().GetEnable()) {
978             FrameReport::GetInstance().EndListFling();
979         }
980 #endif
981         if (scrollEnd_) {
982             scrollEnd_();
983         }
984         currentVelocity_ = 0.0;
985 #if !defined(PREVIEW)
986         LayoutInspector::SupportInspector();
987 #endif
988     }
989 }
990 
ProcessSpringMotion(double position)991 void Scrollable::ProcessSpringMotion(double position)
992 {
993     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "Current Pos is %{public}f, position is %{public}f",
994         currentPos_, position);
995     auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
996     CHECK_NULL_VOID(context);
997     uint64_t currentVsync = context->GetVsyncTime();
998     uint64_t diff = currentVsync - lastVsyncTime_;
999     if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1000         currentVelocity_ = (position - currentPos_) / diff * MILLOS_PER_NANO_SECONDS;
1001     }
1002     lastVsyncTime_ = currentVsync;
1003     if (LessOrEqual(std::abs(currentPos_ - position), 1)) {
1004         // trace stop at OnScrollStop
1005         if (!isFadingAway_) {
1006             AceAsyncTraceBegin(0, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") + nodeTag_).c_str());
1007         }
1008     }
1009     auto distance = currentPos_ - finalPosition_;
1010     auto nextDistance = position - finalPosition_;
1011     isFadingAway_ = GreatNotEqual(std::abs(nextDistance), std::abs(distance));
1012     auto delta = position - currentPos_;
1013     if (distance * nextDistance < 0) {
1014         double currentVelocity = currentVelocity_;
1015         scrollPause_ = true;
1016         MarkNeedFlushAnimationStartTime();
1017         StopSpringAnimation();
1018         ACE_SCOPED_TRACE("change direction in spring animation and start fling animation, distance:%f, "
1019                             "nextDistance:%f, nodeId:%d, tag:%s",
1020             distance, nextDistance, nodeId_, nodeTag_.c_str());
1021         // only handle offsets that are out of bounds
1022         delta = finalPosition_ - currentPos_;
1023         // remainVelocityCallback_ will pass the velocity to the child component
1024         if (!remainVelocityCallback_ || !remainVelocityCallback_(currentVelocity)) {
1025             StartScrollAnimation(position, currentVelocity);
1026         }
1027     }
1028     moved_ = UpdateScrollPosition(delta, SCROLL_FROM_ANIMATION_SPRING);
1029     if (!moved_) {
1030         StopSpringAnimation();
1031     } else if (!touchUp_) {
1032         if (scrollTouchUpCallback_) {
1033             scrollTouchUpCallback_();
1034         }
1035         touchUp_ = true;
1036     }
1037     currentPos_ = position;
1038 }
1039 
ProcessScrollMotion(double position)1040 void Scrollable::ProcessScrollMotion(double position)
1041 {
1042     currentVelocity_ = frictionVelocity_;
1043     if (needScrollSnapToSideCallback_) {
1044         needScrollSnapChange_ = needScrollSnapToSideCallback_(position - currentPos_);
1045     }
1046     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "position is %{public}f, currentVelocity_ is %{public}f, "
1047         "needScrollSnapChange_ is %{public}u",
1048         position, currentVelocity_, needScrollSnapChange_);
1049     if (LessOrEqual(std::abs(currentPos_ - position), 1)) {
1050         // trace stop at OnScrollStop
1051         AceAsyncTraceBegin(0, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") + nodeTag_).c_str());
1052     }
1053     // UpdateScrollPosition return false, means reach to scroll limit.
1054     auto mainDelta = position - currentPos_;
1055     HandleScroll(mainDelta, SCROLL_FROM_ANIMATION, NestedState::GESTURE);
1056     if (!moved_) {
1057         ResetContinueDragCount();
1058         StopFrictionAnimation();
1059     } else if (!touchUp_) {
1060         if (scrollTouchUpCallback_) {
1061             scrollTouchUpCallback_();
1062         }
1063         touchUp_ = true;
1064     }
1065     currentPos_ = position;
1066 
1067     // spring effect special process
1068     if ((IsSnapStopped() && canOverScroll_) || needScrollSnapChange_ ||
1069         (!Container::IsCurrentUseNewPipeline() && outBoundaryCallback_ && outBoundaryCallback_())) {
1070         ACE_SCOPED_TRACE("scrollPause set true to stop ProcessScrollMotion, canOverScroll:%u, needScrollSnapChange:%u, "
1071             "nodeId:%d, tag:%s", canOverScroll_, needScrollSnapChange_, nodeId_, nodeTag_.c_str());
1072         scrollPause_ = true;
1073         skipRestartSpring_ = true;
1074         ResetContinueDragCount();
1075         MarkNeedFlushAnimationStartTime();
1076         StopFrictionAnimation();
1077     }
1078 }
1079 
UpdateScrollPosition(const double offset,int32_t source) const1080 bool Scrollable::UpdateScrollPosition(const double offset, int32_t source) const
1081 {
1082     bool ret = true;
1083     if (callback_) {
1084         ret = callback_(offset, source);
1085     }
1086     return ret;
1087 }
1088 
ProcessScrollOverCallback(double velocity)1089 void Scrollable::ProcessScrollOverCallback(double velocity)
1090 {
1091     if (outBoundaryCallback_ && !outBoundaryCallback_() && !canOverScroll_) {
1092         return;
1093     }
1094     // In the case of chain animation enabled, you need to switch the control point first,
1095     // and then correct the offset value in notification process
1096     if (notifyScrollOverCallback_) {
1097         notifyScrollOverCallback_(velocity);
1098     }
1099     // then use corrected offset to make scroll motion.
1100     if (scrollOverCallback_) {
1101         scrollOverCallback_(velocity);
1102     }
1103 }
1104 
HandleOverScroll(double velocity)1105 bool Scrollable::HandleOverScroll(double velocity)
1106 {
1107     if (!overScrollCallback_) {
1108         if (edgeEffect_ == EdgeEffect::SPRING) {
1109             ProcessScrollOverCallback(velocity);
1110             return true;
1111         }
1112         if (scrollEndCallback_) {
1113             scrollEndCallback_();
1114         }
1115         return false;
1116     }
1117     // call NestableScrollContainer::HandleOverScroll
1118     return overScrollCallback_(velocity);
1119 }
1120 
SetSlipFactor(double SlipFactor)1121 void Scrollable::SetSlipFactor(double SlipFactor)
1122 {
1123     slipFactor_ = std::clamp(SlipFactor, -ADJUSTABLE_VELOCITY, ADJUSTABLE_VELOCITY);
1124 }
1125 
UpdateScrollSnapEndWithOffset(double offset)1126 void Scrollable::UpdateScrollSnapEndWithOffset(double offset)
1127 {
1128     if (!isSnapScrollAnimationStop_) {
1129         MarkNeedFlushAnimationStartTime();
1130         AnimationOption option;
1131         option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
1132         auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
1133         option.SetCurve(curve);
1134         if (!snapOffsetProperty_) {
1135             GetSnapProperty();
1136         }
1137         updateSnapAnimationCount_++;
1138         endPos_ -= offset;
1139         snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
1140         AnimationUtils::StartAnimation(
1141             option,
1142             [weak = AceType::WeakClaim(this)]() {
1143                 auto scroll = weak.Upgrade();
1144                 CHECK_NULL_VOID(scroll);
1145                 scroll->snapOffsetProperty_->Set(scroll->endPos_);
1146                 scroll->isSnapScrollAnimationStop_ = false;
1147             },
1148             [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1149                 ContainerScope scope(id);
1150                 auto scroll = weak.Upgrade();
1151                 CHECK_NULL_VOID(scroll);
1152                 scroll->updateSnapAnimationCount_--;
1153                 // avoid current animation being interrupted by the prev animation's finish callback
1154                 if (scroll->updateSnapAnimationCount_ == 0) {
1155                     scroll->isSnapScrollAnimationStop_ = true;
1156                     scroll->ProcessScrollSnapStop();
1157                 }
1158         });
1159         isSnapScrollAnimationStop_ = false;
1160     }
1161 }
1162 
GetFrictionProperty()1163 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetFrictionProperty()
1164 {
1165     auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1166         auto scroll = weak.Upgrade();
1167         CHECK_NULL_VOID(scroll);
1168         if (scroll->isFrictionAnimationStop_ || scroll->isTouching_) {
1169             return;
1170         }
1171         scroll->isSnapAnimation_ = false;
1172         scroll->ProcessScrollMotion(position);
1173         if (NearEqual(scroll->finalPosition_, position, 1.0)) {
1174             scroll->StopFrictionAnimation();
1175         }
1176         auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
1177         CHECK_NULL_VOID(context);
1178         uint64_t currentVsync = context->GetVsyncTime();
1179         uint64_t diff = currentVsync - scroll->lastVsyncTime_;
1180         if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1181             scroll->frictionVelocity_ = (position - scroll->lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1182             if (NearZero(scroll->frictionVelocity_, FRICTION_VELOCITY_THRESHOLD)) {
1183                 scroll->StopFrictionAnimation();
1184                 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
1185             }
1186         }
1187         scroll->lastVsyncTime_ = currentVsync;
1188         scroll->lastPosition_ = position;
1189     };
1190     frictionOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1191     return frictionOffsetProperty_;
1192 }
1193 
GetSpringProperty()1194 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetSpringProperty()
1195 {
1196     auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1197         auto scroll = weak.Upgrade();
1198         CHECK_NULL_VOID(scroll);
1199         if (scroll->isSpringAnimationStop_) {
1200             return;
1201         }
1202         if (!NearEqual(scroll->finalPosition_, position, SPRING_ACCURACY)) {
1203             scroll->ProcessSpringMotion(position);
1204             return;
1205         }
1206         /*
1207          * In order to prevent accumulation errors, the current position is re obtained to ensure that
1208          * the last frame can accurately stop at the top and bottom positions.
1209          */
1210         if (scroll->currentPositionCallback_) {
1211             double currPos = scroll->currentPositionCallback_();
1212             if (NearEqual(currPos, scroll->currentPos_, 0.5)) {
1213                 scroll->currentPos_ = currPos;
1214             }
1215         }
1216         scroll->ProcessSpringMotion(scroll->finalPosition_);
1217         scroll->StopSpringAnimation();
1218     };
1219     springOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1220     return springOffsetProperty_;
1221 }
1222 
GetSnapProperty()1223 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetSnapProperty()
1224 {
1225     auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1226         auto scroll = weak.Upgrade();
1227         CHECK_NULL_VOID(scroll);
1228         if (scroll->isSnapScrollAnimationStop_ && scroll->isSnapAnimationStop_) {
1229             return;
1230         }
1231         auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
1232         CHECK_NULL_VOID(context);
1233         uint64_t currentVsync = context->GetVsyncTime();
1234         uint64_t diff = currentVsync - scroll->lastVsyncTime_;
1235         if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1236             scroll->snapVelocity_ = (position - scroll->currentPos_) / diff * MILLOS_PER_NANO_SECONDS;
1237         }
1238         scroll->lastVsyncTime_ = currentVsync;
1239         if (NearEqual(scroll->endPos_, position, SPRING_ACCURACY)) {
1240             if (!scroll->isSnapScrollAnimationStop_) {
1241                 scroll->ProcessScrollSnapMotion(scroll->endPos_);
1242             } else if (!scroll->isSnapAnimationStop_) {
1243                 scroll->isSnapAnimation_ = true;
1244                 scroll->ProcessScrollMotion(scroll->endPos_);
1245             }
1246             scroll->StopSnapAnimation();
1247         } else {
1248             if (!scroll->isSnapScrollAnimationStop_) {
1249                 scroll->ProcessScrollSnapMotion(position);
1250             } else if (!scroll->isSnapAnimationStop_) {
1251                 scroll->isSnapAnimation_ = true;
1252                 scroll->ProcessScrollMotion(position);
1253             }
1254         }
1255     };
1256     snapOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1257     return snapOffsetProperty_;
1258 }
1259 
StopFrictionAnimation()1260 void Scrollable::StopFrictionAnimation()
1261 {
1262     if (!isFrictionAnimationStop_) {
1263         ACE_SCOPED_TRACE("StopFrictionAnimation, id:%d, tag:%s", nodeId_, nodeTag_.c_str());
1264         isFrictionAnimationStop_ = true;
1265         AnimationOption option;
1266         option.SetCurve(Curves::EASE);
1267         option.SetDuration(0);
1268         AnimationUtils::StartAnimation(
1269             option,
1270             [weak = AceType::WeakClaim(this)]() {
1271                 auto scroll = weak.Upgrade();
1272                 CHECK_NULL_VOID(scroll);
1273                 scroll->frictionOffsetProperty_->Set(scroll->currentPos_);
1274             },
1275             nullptr);
1276     }
1277 }
1278 
StopSpringAnimation(bool reachFinalPosition)1279 void Scrollable::StopSpringAnimation(bool reachFinalPosition)
1280 {
1281     if (!isSpringAnimationStop_) {
1282         ACE_SCOPED_TRACE(
1283             "StopSpringAnimation, reachFinalPosition:%u, id:%d, tag:%s", reachFinalPosition, nodeId_, nodeTag_.c_str());
1284         isSpringAnimationStop_ = true;
1285         isFadingAway_ = false;
1286         AnimationOption option;
1287         option.SetCurve(Curves::EASE);
1288         option.SetDuration(0);
1289         AnimationUtils::StartAnimation(
1290             option,
1291             [weak = AceType::WeakClaim(this), reachFinalPosition]() {
1292                 auto scroll = weak.Upgrade();
1293                 CHECK_NULL_VOID(scroll);
1294                 if (reachFinalPosition) {
1295                     // ensure that the spring animation is restored to its final position.
1296                     scroll->ProcessSpringMotion(scroll->finalPosition_);
1297                     // use non final position to stop animation, otherwise the animation cannot be stoped.
1298                     scroll->springOffsetProperty_->Set(scroll->finalPosition_ - 1.f);
1299                 } else {
1300                     // avoid top edge spring can not stop
1301                     scroll->springOffsetProperty_->Set(scroll->currentPos_);
1302                 }
1303             },
1304             nullptr);
1305     }
1306     currentVelocity_ = 0.0;
1307 }
1308 
StopSnapAnimation()1309 void Scrollable::StopSnapAnimation()
1310 {
1311     if (!isSnapAnimationStop_ || !isSnapScrollAnimationStop_) {
1312         ACE_SCOPED_TRACE("StopSnapAnimation, isSnapAnimationStop_:%u, isSnapScrollAnimationStop_:%u, id:%d, tag:%s",
1313             isSnapAnimationStop_, isSnapScrollAnimationStop_, nodeId_, nodeTag_.c_str());
1314         isSnapAnimationStop_ = true;
1315         isSnapScrollAnimationStop_ = true;
1316         AnimationOption option;
1317         option.SetCurve(Curves::EASE);
1318         option.SetDuration(0);
1319         AnimationUtils::StartAnimation(
1320             option,
1321             [weak = AceType::WeakClaim(this)]() {
1322                 auto scroll = weak.Upgrade();
1323                 CHECK_NULL_VOID(scroll);
1324                 scroll->snapOffsetProperty_->Set(scroll->currentPos_);
1325             },
1326             nullptr);
1327     }
1328 }
1329 
IsMouseWheelScroll(const GestureEvent & info)1330 inline bool Scrollable::IsMouseWheelScroll(const GestureEvent& info)
1331 {
1332     return info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() != SourceTool::TOUCHPAD;
1333 }
1334 
OnCollectTouchTarget(TouchTestResult & result,const RefPtr<FrameNode> & frameNode,const RefPtr<TargetComponent> & targetComponent,ResponseLinkResult & responseLinkResult)1335 void Scrollable::OnCollectTouchTarget(TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
1336     const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult)
1337 {
1338     if (panRecognizerNG_) {
1339         panRecognizerNG_->SetNodeId(frameNode->GetId());
1340         panRecognizerNG_->AttachFrameNode(frameNode);
1341         panRecognizerNG_->SetTargetComponent(targetComponent);
1342         panRecognizerNG_->SetIsSystemGesture(true);
1343         panRecognizerNG_->SetRecognizerType(GestureTypeName::PAN_GESTURE);
1344         result.emplace_back(panRecognizerNG_);
1345         responseLinkResult.emplace_back(panRecognizerNG_);
1346     }
1347 }
1348 } // namespace OHOS::Ace::NG
1349