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