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