• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/scrollable/scrollable.h"
17 
18 #include "base/log/jank_frame_report.h"
19 #include "base/perfmonitor/perf_constants.h"
20 #include "base/perfmonitor/perf_monitor.h"
21 #include "base/ressched/ressched_report.h"
22 #include "core/common/layout_inspector.h"
23 #include "core/components_ng/pattern/scrollable/scrollable_theme.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25 
26 namespace OHOS::Ace::NG {
27 namespace {
28 
29 constexpr double CAP_COEFFICIENT = 0.45;
30 constexpr int32_t FIRST_THRESHOLD = 4;
31 constexpr int32_t SECOND_THRESHOLD = 10;
32 constexpr double CAP_FIXED_VALUE = 16.0;
33 constexpr uint32_t DRAG_INTERVAL_TIME = 400;
34 constexpr uint32_t MULTI_FLING_DISTANCE = 125;
35 
36 #ifndef WEARABLE_PRODUCT
37 constexpr double FRICTION = 0.6;
38 constexpr double API11_FRICTION = 0.7;
39 constexpr double API12_FRICTION = 0.75;
40 constexpr double SLOW_FRICTION_THRESHOLD = 3000.0;
41 constexpr double SLOW_FRICTION = 1.0;
42 constexpr double VELOCITY_SCALE = 1.0;
43 constexpr double SLOW_VELOCITY_SCALE = 1.2;
44 constexpr double NEW_VELOCITY_SCALE = 1.5;
45 constexpr double ADJUSTABLE_VELOCITY = 3000.0;
46 #else
47 constexpr double DISTANCE_EPSILON = 1.0;
48 constexpr double FRICTION = 0.9;
49 constexpr double VELOCITY_SCALE = 0.8;
50 constexpr double ADJUSTABLE_VELOCITY = 0.0;
51 #endif
52 constexpr float FRICTION_SCALE = -4.2f;
53 constexpr uint32_t CUSTOM_SPRING_ANIMATION_DURATION = 1000;
54 constexpr uint64_t MILLOS_PER_NANO_SECONDS = 1000 * 1000 * 1000;
55 constexpr uint64_t MIN_DIFF_VSYNC = 1000 * 1000; // min is 1ms
56 constexpr float DEFAULT_THRESHOLD = 0.75f;
57 constexpr float DEFAULT_SPRING_RESPONSE = 0.416f;
58 constexpr float DEFAULT_SPRING_DAMP = 0.99f;
59 constexpr uint32_t MAX_VSYNC_DIFF_TIME = 100 * 1000 * 1000; // max 100 ms
60 constexpr float START_FRICTION_VELOCITY_THRESHOLD = 240.0f;
61 constexpr float FRICTION_VELOCITY_THRESHOLD = 120.0f;
62 constexpr float SPRING_ACCURACY = 0.1;
63 constexpr float DEFAULT_MINIMUM_AMPLITUDE_PX = 1.0f;
64 constexpr float SCROLL_SNAP_MIN_STEP_THRESHOLD = 10.0f;
65 constexpr float SCROLL_SNAP_MIN_STEP = 1.0f;
66 #ifdef OHOS_PLATFORM
67 constexpr int64_t INCREASE_CPU_TIME_ONCE = 4000000000; // 4s(unit: ns)
68 #endif
69 
70 #ifdef SUPPORT_DIGITAL_CROWN
71 constexpr double ANGULAR_VELOCITY_FACTOR  = 0.001f;
72 constexpr float ANGULAR_VELOCITY_SLOW = 0.07f;
73 constexpr float ANGULAR_VELOCITY_MEDIUM = 0.2f;
74 constexpr float ANGULAR_VELOCITY_FAST = 0.54f;
75 constexpr float DISPLAY_CONTROL_RATIO_VERY_SLOW = 1.19f;
76 constexpr float DISPLAY_CONTROL_RATIO_SLOW = 1.87f;
77 constexpr float DISPLAY_CONTROL_RATIO_MEDIUM = 1.67f;
78 constexpr float DISPLAY_CONTROL_RATIO_FAST = 1.59f;
79 constexpr float CROWN_SENSITIVITY_LOW = 0.8f;
80 constexpr float CROWN_SENSITIVITY_MEDIUM = 1.0f;
81 constexpr float CROWN_SENSITIVITY_HIGH = 1.2f;
82 constexpr float RESPONSIVE_SPRING_AMPLITUDE_RATIO = 0.00025f;
83 
84 constexpr float CROWN_START_FRICTION_VELOCITY_THRESHOLD = 6.0f;
85 #else
86 constexpr float RESPONSIVE_SPRING_AMPLITUDE_RATIO = 0.001f;
87 #endif
88 } // namespace
89 
GetVelocityScale()90 double Scrollable::GetVelocityScale()
91 {
92     return Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_VELOCITY_SCALE
93                                                                                     : VELOCITY_SCALE;
94 }
95 
~Scrollable()96 Scrollable::~Scrollable()
97 {
98     // If animation still runs, force stop it.
99     if (!IsStopped()) {
100         PerfMonitor::GetPerfMonitor()->EndCommercial(PerfConstants::APP_LIST_FLING, false);
101         AceAsyncTraceEndCommercial(0, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") +
102             nodeTag_).c_str());
103         if (!context_.Invalid()) {
104             auto context = context_.Upgrade();
105             context->SetUiDvsyncSwitch(false);
106         }
107     }
108     StopFrictionAnimation();
109     StopSpringAnimation();
110     StopSnapAnimation();
111 }
112 
Initialize(const RefPtr<FrameNode> & host)113 void Scrollable::Initialize(const RefPtr<FrameNode>& host)
114 {
115     CHECK_NULL_VOID(host);
116     weakHost_ = host;
117     auto pipeline = host->GetContextRefPtr();
118     context_ = pipeline;
119     InitPanRecognizerNG();
120     available_ = true;
121     CHECK_NULL_VOID(pipeline);
122     auto scrollableTheme = pipeline->GetTheme<ScrollableTheme>();
123     CHECK_NULL_VOID(scrollableTheme);
124     flingVelocityScale_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN)
125                               ? scrollableTheme->GetFlingVelocityScale()
126                               : VELOCITY_SCALE;
127     springVelocityScale_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN)
128                                ? scrollableTheme->GetSpringVelocityScale()
129                                : VELOCITY_SCALE;
130     ratio_ = scrollableTheme->GetRatio();
131     springResponse_ = scrollableTheme->GetSpringResponse();
132     touchPadVelocityScaleRate_ = scrollableTheme->GetTouchPadVelocityScaleRate();
133     if (friction_ == -1) {
134         InitFriction(scrollableTheme->GetFriction());
135     }
136 }
137 
InitAxisAnimator()138 void Scrollable::InitAxisAnimator()
139 {
140     CHECK_NULL_VOID(!axisAnimator_);
141     auto axisAnimationCallback = [weak = WeakClaim(this)](float offset) {
142         auto scrollable = weak.Upgrade();
143         CHECK_NULL_VOID(scrollable);
144         scrollable->ReportToDragFRCScene(scrollable->currentVelocity_, NG::SceneStatus::RUNNING);
145         scrollable->ProcessScrollMotion(offset, SCROLL_FROM_AXIS);
146     };
147     auto axisAnimationStartCallback = [weak = WeakClaim(this)](float position) {
148         auto scrollable = weak.Upgrade();
149         CHECK_NULL_VOID(scrollable && scrollable->onScrollStartRec_);
150         scrollable->ReportToDragFRCScene(scrollable->currentVelocity_, NG::SceneStatus::START);
151         scrollable->onScrollStartRec_(position);
152     };
153     auto axisAnimationFinishCallback = [weak = WeakClaim(this)]() {
154         auto scrollable = weak.Upgrade();
155         CHECK_NULL_VOID(scrollable);
156         scrollable->ReportToDragFRCScene(scrollable->currentVelocity_, NG::SceneStatus::END);
157         scrollable->ProcessScrollMotionStop();
158     };
159     axisAnimator_ = AceType::MakeRefPtr<AxisAnimator>(std::move(axisAnimationCallback),
160         std::move(axisAnimationStartCallback), std::move(axisAnimationFinishCallback));
161     axisAnimator_->Initialize(context_);
162 }
163 
InitPanRecognizerNG()164 void Scrollable::InitPanRecognizerNG()
165 {
166     PanDirection panDirection;
167     panDirection.type = axis_ == Axis::VERTICAL ? PanDirection::VERTICAL : PanDirection::HORIZONTAL;
168     double distance = SystemProperties::GetScrollableDistance();
169     if (LessOrEqual(distance, 0.0)) {
170         distance = DEFAULT_PAN_DISTANCE.ConvertToPx();
171     }
172     panRecognizerNG_ =
173         AceType::MakeRefPtr<NG::PanRecognizer>(DEFAULT_PAN_FINGER, panDirection, distance);
174     panRecognizerNG_->SetIsAllowMouse(false);
175     SetOnActionStart();
176     SetOnActionUpdate();
177     SetOnActionEnd();
178     SetOnActionCancel();
179     SetPanEndCallback();
180 }
181 
SetOnActionStart()182 void Scrollable::SetOnActionStart()
183 {
184     CHECK_NULL_VOID(panRecognizerNG_);
185     auto actionStart = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
186         auto scroll = weakScroll.Upgrade();
187         CHECK_NULL_VOID(scroll);
188         scroll->HandleDragStart(info);
189     };
190     panRecognizerNG_->SetOnActionStart(actionStart);
191 }
192 
SetOnActionUpdate()193 void Scrollable::SetOnActionUpdate()
194 {
195     CHECK_NULL_VOID(panRecognizerNG_);
196     auto actionUpdate = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
197         auto scroll = weakScroll.Upgrade();
198         CHECK_NULL_VOID(scroll);
199         scroll->HandleDragUpdate(info);
200     };
201     panRecognizerNG_->SetOnActionUpdate(actionUpdate);
202 }
203 
SetOnActionEnd()204 void Scrollable::SetOnActionEnd()
205 {
206     CHECK_NULL_VOID(panRecognizerNG_);
207     auto actionEnd = [weakScroll = AceType::WeakClaim(this)](GestureEvent& info) {
208         auto scroll = weakScroll.Upgrade();
209         CHECK_NULL_VOID(scroll);
210         scroll->HandleDragEnd(info);
211         scroll->ProcessPanActionEndEvents(info);
212         scroll->isDragging_ = false;
213     };
214     panRecognizerNG_->SetOnActionEnd(actionEnd);
215 }
216 
SetPanEndCallback()217 void Scrollable::SetPanEndCallback()
218 {
219     CHECK_NULL_VOID(panRecognizerNG_);
220     auto panEndCallback = [weakScroll = AceType::WeakClaim(this)](GestureEvent& info) {
221         auto scroll = weakScroll.Upgrade();
222         if (scroll) {
223             auto tempInfo = info;
224             tempInfo.SetMainDelta(0.0);
225             tempInfo.SetMainVelocity(0.0);
226             ACE_SCOPED_TRACE("Trigger PanEndCallback, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
227             scroll->HandleDragEnd(tempInfo, true);
228             scroll->ProcessPanActionEndEvents(tempInfo);
229             scroll->isDragging_ = false;
230         }
231     };
232     panRecognizerNG_->SetPanEndCallback(panEndCallback);
233 }
234 
ProcessPanActionEndEvents(const GestureEvent & info)235 void Scrollable::ProcessPanActionEndEvents(const GestureEvent& info)
236 {
237     CHECK_NULL_VOID(!panActionEndEvents_.empty());
238     std::for_each(panActionEndEvents_.begin(), panActionEndEvents_.end(), [info](GestureEventFunc& event) {
239         auto gestureInfo = info;
240         event(gestureInfo);
241     });
242 }
243 
SetOnActionCancel()244 void Scrollable::SetOnActionCancel()
245 {
246     CHECK_NULL_VOID(panRecognizerNG_);
247     auto actionCancel = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
248         auto scroll = weakScroll.Upgrade();
249         CHECK_NULL_VOID(scroll);
250         if (scroll->dragCancelCallback_) {
251             scroll->dragCancelCallback_();
252         }
253         GestureEvent nullInfo;
254         scroll->HandleDragEnd(nullInfo);
255         if (scroll->panActionEndEvents_.empty()) {
256             scroll->isDragging_ = false;
257             return;
258         }
259         std::for_each(scroll->panActionEndEvents_.begin(), scroll->panActionEndEvents_.end(),
260             [nullInfo](GestureEventFunc& event) {
261                 auto gestureInfo = nullInfo;
262                 event(gestureInfo);
263             });
264         scroll->isDragging_ = false;
265     };
266     panRecognizerNG_->SetOnActionCancel(actionCancel);
267 }
268 
269 #ifdef SUPPORT_DIGITAL_CROWN
ListenDigitalCrownEvent(const RefPtr<FrameNode> & frameNode)270 void Scrollable::ListenDigitalCrownEvent(const RefPtr<FrameNode>& frameNode)
271 {
272     CHECK_NULL_VOID(frameNode);
273     auto focusHub = frameNode->GetFocusHub();
274     CHECK_NULL_VOID(focusHub);
275 
276     auto onCrownEvent = [weakScroll = AceType::WeakClaim(this), weakNode = AceType::WeakClaim(AceType::RawPtr(
277         frameNode))](const CrownEvent& event) -> bool {
278         auto scroll = weakScroll.Upgrade();
279         CHECK_NULL_RETURN(scroll, false);
280         auto node = weakNode.Upgrade();
281         CHECK_NULL_RETURN(node, false);
282 
283         auto centerOffset = node->GetGeometryNode()->GetContentRect().Center();
284         scroll->HandleCrownEvent(event, centerOffset);
285         return true;
286     };
287     focusHub->SetOnCrownEventInternal(std::move(onCrownEvent));
288 
289     focusHub->SetOnBlurReasonInternal([weak = WeakClaim(this)](const BlurReason& blurReason) {
290         auto scroll = weak.Upgrade();
291         CHECK_NULL_VOID(scroll);
292         GestureEvent info;
293         info.SetSourceDevice(SourceType::CROWN);
294         info.SetSourceTool(SourceTool::UNKNOWN);
295         scroll->HandleCrownActionCancel(info);
296     });
297 }
298 
GetCrownRotatePx(const CrownEvent & event) const299 double Scrollable::GetCrownRotatePx(const CrownEvent& event) const
300 {
301     double velocity = std::abs(event.angularVelocity * ANGULAR_VELOCITY_FACTOR);
302     double px = 0.0;
303     if (LessOrEqualCustomPrecision(velocity, ANGULAR_VELOCITY_SLOW, 0.01f)) {  // very slow
304         px = (Dimension(DISPLAY_CONTROL_RATIO_VERY_SLOW, DimensionUnit::VP) * event.degree).ConvertToPx();
305     } else if (LessOrEqualCustomPrecision(velocity, ANGULAR_VELOCITY_MEDIUM, 0.01f)) {  // slow
306         px = (Dimension(DISPLAY_CONTROL_RATIO_SLOW, DimensionUnit::VP) * event.degree).ConvertToPx();
307     } else if (LessOrEqualCustomPrecision(velocity, ANGULAR_VELOCITY_FAST, 0.01f)) {  // medium
308         px = (Dimension(DISPLAY_CONTROL_RATIO_MEDIUM, DimensionUnit::VP) * event.degree).ConvertToPx();
309     } else {  // fast
310         px = (Dimension(DISPLAY_CONTROL_RATIO_FAST, DimensionUnit::VP) * event.degree).ConvertToPx();
311     }
312     switch (crownSensitivity_) {
313         case CrownSensitivity::LOW:
314             px *= CROWN_SENSITIVITY_LOW;
315             break;
316         case CrownSensitivity::MEDIUM:
317             px *= CROWN_SENSITIVITY_MEDIUM;
318             break;
319         case CrownSensitivity::HIGH:
320             px *= CROWN_SENSITIVITY_HIGH;
321             break;
322         default:
323             break;
324     }
325     return px;
326 }
327 
UpdateCrownVelocity(const TimeStamp & timeStamp,double mainDelta,bool end)328 void Scrollable::UpdateCrownVelocity(const TimeStamp& timeStamp, double mainDelta, bool end)
329 {
330     if (axis_ == Axis::VERTICAL) {
331         accumulativeCrownPx_ += Offset(0, mainDelta);
332     } else {
333         accumulativeCrownPx_ += Offset(mainDelta, 0);
334     }
335     crownVelocityTracker_.UpdateTrackerPoint(accumulativeCrownPx_.GetX(), accumulativeCrownPx_.GetY(), timeStamp, end);
336 }
337 
HandleCrownEvent(const CrownEvent & event,const OffsetF & center)338 void Scrollable::HandleCrownEvent(const CrownEvent& event, const OffsetF& center)
339 {
340     DimensionOffset centerDimension(center);
341     Offset globalLocation(centerDimension.GetX().ConvertToPx(), centerDimension.GetY().ConvertToPx());
342 
343     GestureEvent info;
344     info.SetSourceDevice(SourceType::CROWN);
345     info.SetSourceTool(SourceTool::UNKNOWN);
346     info.SetGlobalLocation(globalLocation);
347     double mainDelta = GetCrownRotatePx(event);
348 
349     switch (event.action) {
350         case CrownAction::BEGIN:
351             crownEventNum_ = 0;
352             TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "-->BEGIN]");
353             HandleCrownActionBegin(event.timeStamp, mainDelta, info);
354             break;
355         case CrownAction::UPDATE:
356             TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "-->UPDATE]");
357             HandleCrownActionUpdate(event.timeStamp, mainDelta, info);
358             break;
359         case CrownAction::END:
360             TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "-->END]");
361             HandleCrownActionEnd(event.timeStamp, mainDelta, info);
362             break;
363         default:
364             HandleCrownActionCancel(info);
365             break;
366     }
367 }
368 
HandleCrownActionBegin(const TimeStamp & timeStamp,double mainDelta,GestureEvent & info)369 void Scrollable::HandleCrownActionBegin(const TimeStamp& timeStamp, double mainDelta, GestureEvent& info)
370 {
371     accumulativeCrownPx_.Reset();
372     crownVelocityTracker_.Reset();
373     UpdateCrownVelocity(timeStamp, mainDelta, false);
374     info.SetMainDelta(mainDelta);
375     info.SetMainVelocity(crownVelocityTracker_.GetMainAxisVelocity());
376     isDragging_ = true;
377     isCrownEventDragging_ = true;
378     isCrownDragging_ = true;
379     HandleDragStart(info);
380 }
381 
HandleCrownActionUpdate(const TimeStamp & timeStamp,double mainDelta,GestureEvent & info)382 void Scrollable::HandleCrownActionUpdate(const TimeStamp& timeStamp, double mainDelta, GestureEvent& info)
383 {
384     if (!isCrownEventDragging_) {
385         return;
386     }
387     UpdateCrownVelocity(timeStamp, mainDelta, false);
388     info.SetMainDelta(mainDelta);
389     info.SetMainVelocity(crownVelocityTracker_.GetMainAxisVelocity());
390     HandleDragUpdate(info);
391 }
392 
HandleCrownActionEnd(const TimeStamp & timeStamp,double mainDelta,GestureEvent & info)393 void Scrollable::HandleCrownActionEnd(const TimeStamp& timeStamp, double mainDelta, GestureEvent& info)
394 {
395     if (!isCrownEventDragging_) {
396         return;
397     }
398     if (NearZero(mainDelta)) {
399         info.SetMainDelta(crownVelocityTracker_.GetMainAxisDeltaPos());
400         info.SetMainVelocity(crownVelocityTracker_.GetMainAxisVelocity());
401     } else {
402         UpdateCrownVelocity(timeStamp, mainDelta, true);
403         info.SetMainDelta(mainDelta);
404         info.SetMainVelocity(crownVelocityTracker_.GetMainAxisVelocity());
405     }
406     HandleDragEnd(info);
407     std::for_each(panActionEndEvents_.begin(), panActionEndEvents_.end(),
408         [info](GestureEventFunc& event) {
409             auto gestureInfo = info;
410             event(gestureInfo);
411         });
412     isDragging_ = false;
413     isCrownDragging_ = false;
414     auto context = context_.Upgrade();
415     CHECK_NULL_VOID(context);
416     auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
417     crownTask_.Reset([weak = WeakClaim(this)] {
418         auto scrollable = weak.Upgrade();
419         if (scrollable) {
420             scrollable->SetCrownEventDragging(false);
421         }
422     });
423     taskExecutor.PostDelayedTask(crownTask_, CUSTOM_SPRING_ANIMATION_DURATION, "ArkUIUpdateCrownEventDrag");
424 }
425 
HandleCrownActionCancel(GestureEvent & info)426 void Scrollable::HandleCrownActionCancel(GestureEvent& info)
427 {
428     if (!isDragging_ || !isCrownEventDragging_) {
429         return;
430     }
431 
432     if (dragCancelCallback_) {
433         dragCancelCallback_();
434     }
435     info.SetMainDelta(0);
436     info.SetMainVelocity(0);
437     HandleDragEnd(info);
438     std::for_each(panActionEndEvents_.begin(), panActionEndEvents_.end(),
439         [info](GestureEventFunc& event) {
440             auto gestureInfo = info;
441             event(gestureInfo);
442         });
443     isDragging_ = false;
444     isCrownDragging_ = false;
445 }
446 #endif
447 
SetAxis(Axis axis)448 void Scrollable::SetAxis(Axis axis)
449 {
450     axis_ = axis;
451     PanDirection panDirection;
452     if (axis_ == Axis::NONE) {
453         panDirection.type = PanDirection::NONE;
454     } else if (axis_ == Axis::VERTICAL) {
455         panDirection.type = PanDirection::VERTICAL;
456     } else {
457         panDirection.type = PanDirection::HORIZONTAL;
458     }
459     if (panRecognizerNG_) {
460         panRecognizerNG_->SetDirection(panDirection);
461     }
462 #ifdef SUPPORT_DIGITAL_CROWN
463     crownVelocityTracker_.SetMainAxis(axis_);
464 #endif
465 }
466 
HandleTouchDown(bool fromcrown)467 void Scrollable::HandleTouchDown(bool fromcrown)
468 {
469     if (!fromcrown) {
470         isTouching_ = true;
471     }
472     // If animation still runs, first stop it.
473     ACE_SCOPED_TRACE("HandleTouchDown, panDirection:%u, id:%d, tag:%s", GetPanDirection(), nodeId_, nodeTag_.c_str());
474     StopSpringAnimation();
475     if (state_ == AnimationState::FRICTION) {
476         StopFrictionAnimation();
477     } else if (state_ == AnimationState::SNAP) {
478         StopSnapAnimation();
479     } else {
480         // Resets values.
481         currentPos_ = 0.0;
482     }
483 }
484 
HandleTouchUp()485 void Scrollable::HandleTouchUp()
486 {
487     // Two fingers are alternately drag, one finger is released without triggering spring animation.
488     ACE_SCOPED_TRACE("HandleTouchUp, isDragging_:%u, nestedScrolling_:%u id:%d, tag:%s", isDragging_, nestedScrolling_,
489         nodeId_, nodeTag_.c_str());
490     if (isDragging_) {
491         return;
492     }
493     isTouching_ = false;
494     if (nestedScrolling_) {
495         return;
496     }
497     // outBoundaryCallback_ is only set in ScrollablePattern::SetEdgeEffect and when the edge effect is spring
498     if (outBoundaryCallback_ && outBoundaryCallback_()) {
499         if (state_ != AnimationState::SPRING && scrollOverCallback_) {
500             if (onScrollStartRec_) {
501                 onScrollStartRec_(static_cast<float>(axis_));
502             }
503             ProcessScrollOverCallback(0.0);
504         }
505         return;
506     }
507     if (state_ != AnimationState::SNAP && startSnapAnimationCallback_) {
508         SnapAnimationOptions snapAnimationOptions;
509         startSnapAnimationCallback_(snapAnimationOptions);
510     }
511 }
512 
HandleTouchCancel()513 void Scrollable::HandleTouchCancel()
514 {
515     if (isDragging_) {
516         return;
517     }
518     isTouching_ = false;
519     ACE_SCOPED_TRACE("HandleTouchCancel, id:%d, tag:%s", nodeId_, nodeTag_.c_str());
520     if (state_ != AnimationState::SPRING && scrollOverCallback_) {
521         ProcessScrollOverCallback(0.0);
522     }
523 }
524 
IsAnimationNotRunning() const525 bool Scrollable::IsAnimationNotRunning() const
526 {
527     return !isTouching_ && state_ == AnimationState::IDLE;
528 }
529 
Idle() const530 bool Scrollable::Idle() const
531 {
532     return !isTouching_ && state_ == AnimationState::IDLE && !nestedScrolling_ && !isCrownDragging_;
533 }
534 
IsStopped() const535 bool Scrollable::IsStopped() const
536 {
537     return state_ == AnimationState::IDLE;
538 }
539 
IsSpringStopped() const540 bool Scrollable::IsSpringStopped() const
541 {
542     return state_ != AnimationState::SPRING;
543 }
544 
IsSnapStopped() const545 bool Scrollable::IsSnapStopped() const
546 {
547     return state_ != AnimationState::SNAP;
548 }
549 
StopScrollable()550 void Scrollable::StopScrollable()
551 {
552     if (state_ == AnimationState::FRICTION) {
553         StopFrictionAnimation();
554     }
555     if (state_ == AnimationState::SPRING) {
556         StopSpringAnimation();
557     }
558     if (state_ == AnimationState::SNAP) {
559         StopSnapAnimation();
560     }
561 }
562 
HandleScrollEnd(const std::optional<float> & velocity)563 void Scrollable::HandleScrollEnd(const std::optional<float>& velocity)
564 {
565     // priority:
566     //  1. onScrollEndRec_ (would internally call onScrollEnd)
567     //  2. scrollEndCallback_
568     if (onScrollEndRec_) {
569         onScrollEndRec_(velocity);
570         return;
571     }
572     if (scrollEndCallback_) {
573         scrollEndCallback_();
574     }
575 }
576 
HandleDragStart(const OHOS::Ace::GestureEvent & info)577 void Scrollable::HandleDragStart(const OHOS::Ace::GestureEvent& info)
578 {
579     if (info.GetSourceTool() == SourceTool::TOUCHPAD) {
580         HandleTouchDown();
581     }
582 #ifdef SUPPORT_DIGITAL_CROWN
583     crownTask_.Cancel();
584     if (info.GetSourceDevice() != SourceType::CROWN) {
585         isCrownEventDragging_ = false;
586     }
587     if (isCrownEventDragging_) {
588         HandleTouchDown(true);
589     }
590 #endif
591     currentVelocity_ = info.GetMainVelocity();
592     auto isAxisEvent = IsMouseWheelScroll(info);
593     if (!isAxisEvent) {
594         ReportToDragFRCScene(currentVelocity_, NG::SceneStatus::START);
595     }
596     if (continuousDragStatus_) {
597         IncreaseContinueDragCount();
598         task_.Cancel();
599     }
600     SetDragStartPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
601     const double dragPositionInMainAxis =
602         axis_ == Axis::VERTICAL ? info.GetGlobalLocation().GetY() : info.GetGlobalLocation().GetX();
603     TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Scroll drag start, id:%{public}d, tag:%{public}s", nodeId_, nodeTag_.c_str());
604     skipRestartSpring_ = false; // reset flags. Extract method if more flags need to be reset
605 #ifdef OHOS_PLATFORM
606     // Increase the cpu frequency when sliding start.
607     auto currentTime = GetSysTimestamp();
608     auto increaseCpuTime = currentTime - startIncreaseTime_;
609     if (!moved_ || increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
610         startIncreaseTime_ = currentTime;
611         if (FrameReport::GetInstance().GetEnable()) {
612             FrameReport::GetInstance().BeginListFling();
613         }
614     }
615 #endif
616     JankFrameReport::GetInstance().SetFrameJankFlag(JANK_RUNNING_SCROLL);
617     ACE_SCOPED_TRACE("HandleDragStart, inputEventType:%d, sourceTool:%d, IsMouseWheelScroll:%u, "
618                      "IsAxisAnimationRunning:%u, IsSnapAnimationRunning:%u, id:%d, tag:%s",
619         info.GetInputEventType(), info.GetSourceTool(), isAxisEvent, IsAxisAnimationRunning(), IsSnapAnimationRunning(),
620         nodeId_, nodeTag_.c_str());
621     if (isAxisEvent) {
622         if (!IsAxisAnimationRunning() && !IsSnapAnimationRunning()) {
623             axisSnapDistance_ = currentPos_;
624             snapDirection_ = SnapDirection::NONE;
625         }
626         return;
627     } else if (IsAxisAnimationRunning()) {
628         StopAxisAnimation();
629     }
630     isDragging_ = true;
631     if (onScrollStartRec_) {
632         onScrollStartRec_(static_cast<float>(dragPositionInMainAxis));
633     }
634 }
635 
HandleScroll(double offset,int32_t source,NestedState state)636 ScrollResult Scrollable::HandleScroll(double offset, int32_t source, NestedState state)
637 {
638     if (!handleScrollCallback_) {
639         ExecuteScrollBegin(offset);
640         canOverScroll_ = false;
641         moved_ = UpdateScrollPosition(offset, source);
642         return { 0, false };
643     }
644     // call NestableScrollContainer::HandleScroll
645     return handleScrollCallback_(static_cast<float>(offset), source, state);
646 }
647 
HandleDragUpdate(const GestureEvent & info)648 void Scrollable::HandleDragUpdate(const GestureEvent& info)
649 {
650     currentVelocity_ = info.GetMainVelocity();
651     ReportToDragFRCScene(currentVelocity_, NG::SceneStatus::RUNNING);
652     if (!NearZero(info.GetMainVelocity()) && dragCount_ >= FIRST_THRESHOLD) {
653         if (Negative(lastGestureVelocity_ / info.GetMainVelocity())) {
654             ResetContinueDragCount();
655         }
656     }
657     if (state_ != AnimationState::IDLE) {
658         // If animation still runs, first stop it.
659         isDragUpdateStop_ = true;
660         StopFrictionAnimation();
661         StopSpringAnimation();
662         if (!(IsMouseWheelScroll(info) && GetSnapType() != SnapType::NONE_SNAP)) {
663             StopSnapAnimation();
664             currentPos_ = 0.0;
665         }
666     }
667 #ifdef OHOS_PLATFORM
668     // Handle the case where you keep sliding past limit time(4s).
669     auto currentTime = GetSysTimestamp();
670     auto increaseCpuTime = currentTime - startIncreaseTime_;
671     if (increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
672         startIncreaseTime_ = currentTime;
673         if (FrameReport::GetInstance().GetEnable()) {
674             FrameReport::GetInstance().BeginListFling();
675         }
676     }
677 #endif
678     auto mainDelta = info.GetMainDelta();
679     lastMainDelta_ = mainDelta;
680     auto isReverse = isReverseCallback_ && isReverseCallback_();
681     mainDelta = isReverse ? Round(-mainDelta) : Round(mainDelta);
682     JankFrameReport::GetInstance().RecordFrameUpdate();
683     auto source = SCROLL_FROM_UPDATE;
684     auto isAxisEvent = IsMouseWheelScroll(info);
685     if (isAxisEvent) {
686         source = SCROLL_FROM_AXIS;
687     }
688 #ifdef SUPPORT_DIGITAL_CROWN
689     if (isCrownEventDragging_) {
690         source = SCROLL_FROM_CROWN;
691     }
692 #endif
693     ACE_SCOPED_TRACE(
694         "HandleDragUpdate, mainDelta:%f, source:%d, id:%d, tag:%s", mainDelta, source, nodeId_, nodeTag_.c_str());
695     if (isAxisEvent) {
696         ProcessAxisUpdateEvent(mainDelta);
697         return;
698     }
699     HandleScroll(mainDelta, source, NestedState::GESTURE);
700 }
701 
ProcessAxisUpdateEvent(float mainDelta,bool fromScrollBar)702 void Scrollable::ProcessAxisUpdateEvent(float mainDelta, bool fromScrollBar)
703 {
704     auto context = context_.Upgrade();
705     CHECK_NULL_VOID(context);
706     auto currentVsyncTime = context->GetVsyncTime();
707     auto snapType = GetSnapType();
708     if (snapType != SnapType::NONE_SNAP && startSnapAnimationCallback_) {
709         CHECK_NULL_VOID(lastAxisVsyncTime_ != currentVsyncTime);
710         lastAxisVsyncTime_ = currentVsyncTime;
711         auto snapDelta = 0.f;
712         auto snapDirection = SnapDirection::NONE;
713         auto isInitScroll = (snapType == SnapType::LIST_SNAP && snapDirection_ == SnapDirection::NONE) ||
714                             (snapType == SnapType::SCROLL_SNAP && state_ == AnimationState::IDLE);
715         if (isInitScroll) {
716             snapDirection = Positive(mainDelta) ? SnapDirection::FORWARD : SnapDirection::BACKWARD;
717         } else {
718             if (snapDirection_ == SnapDirection::BACKWARD && !Positive(mainDelta)) {
719                 snapDelta = endPos_ - currentPos_;
720                 snapDirection = SnapDirection::BACKWARD;
721             } else if (snapDirection_ == SnapDirection::FORWARD && Positive(mainDelta)) {
722                 snapDelta = endPos_ - currentPos_;
723                 snapDirection = SnapDirection::FORWARD;
724             } else if ((snapDirection_ == SnapDirection::BACKWARD && Positive(mainDelta)) ||
725                        (snapDirection_ == SnapDirection::FORWARD && !Positive(mainDelta))) {
726                 snapDelta = 0.f;
727                 snapDirection = SnapDirection::NONE;
728             }
729         }
730         ACE_SCOPED_TRACE("ProcessAxisUpdateEvent start SnapAnimation, snapDelta:%f, snapDirection:%d, "
731                          "lastSnapDirection:%d, id:%d, tag:%s",
732             snapDelta, snapDirection, snapDirection_, nodeId_, nodeTag_.c_str());
733         SnapAnimationOptions snapAnimationOptions = { .snapDelta = snapDelta, .animationVelocity = currentVelocity_,
734             .snapDirection = snapDirection, .fromScrollBar = fromScrollBar };
735         startSnapAnimationCallback_(snapAnimationOptions);
736         auto isNeedAdjustDirection = (snapType == SnapType::SCROLL_SNAP && snapDirection == SnapDirection::NONE);
737         if (isNeedAdjustDirection) {
738             snapDirection_ = Positive(mainDelta) ? SnapDirection::FORWARD : SnapDirection::BACKWARD;
739         } else {
740             snapDirection_ = snapDirection;
741         }
742         return;
743     }
744     lastAxisVsyncTime_ = currentVsyncTime;
745     if (!axisAnimator_) {
746         InitAxisAnimator();
747     }
748     ACE_SCOPED_TRACE(
749         "ProcessAxisUpdateEvent onAxis, IsAxisAnimationRunning:%u, mainDelta:%f, currentPos_:%f, id:%d, tag:%s",
750         IsAxisAnimationRunning(), mainDelta, currentPos_, nodeId_, nodeTag_.c_str());
751     axisAnimator_->OnAxis(mainDelta, currentPos_);
752 }
753 
LayoutDirectionEst(double gestureVelocity,double velocityScale,bool isScrollFromTouchPad)754 void Scrollable::LayoutDirectionEst(double gestureVelocity, double velocityScale, bool isScrollFromTouchPad)
755 {
756     auto defaultVelocityScale = isSlow_ ? SLOW_VELOCITY_SCALE : velocityScale;
757     double ret = SystemProperties::GetSrollableVelocityScale();
758     velocityScale = !NearZero(ret) ? ret : defaultVelocityScale;
759     velocityScale = isScrollFromTouchPad ? velocityScale * touchPadVelocityScaleRate_ : velocityScale;
760     if (isReverseCallback_ && isReverseCallback_()) {
761         currentVelocity_ = -gestureVelocity * velocityScale * GetGain(GetDragOffset());
762     } else {
763         currentVelocity_ = gestureVelocity * velocityScale * GetGain(GetDragOffset());
764     }
765     // Apply max fling velocity limit, it must be calculated after all fling velocity gain.
766     currentVelocity_ = std::clamp(currentVelocity_, -maxFlingVelocity_ + slipFactor_, maxFlingVelocity_ - slipFactor_);
767 }
768 
HandleDragEnd(const GestureEvent & info,bool isFromPanEnd)769 void Scrollable::HandleDragEnd(const GestureEvent& info, bool isFromPanEnd)
770 {
771     TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Scroll drag end, velocity is %{public}f id:%{public}d, tag:%{public}s, "
772         "dragCnt:%{public}d", info.GetMainVelocity(), nodeId_, nodeTag_.c_str(), dragCount_);
773     auto isAxisEvent = IsMouseWheelScroll(info);
774     if (isAxisEvent) {
775         ProcessAxisEndEvent();
776         return;
777     }
778     // avoid no render frame when drag end
779     if (!isFromPanEnd) {
780         if (NearZero(info.GetMainDelta())) {
781             auto tempInfo = info;
782             tempInfo.SetMainDelta(lastMainDelta_);
783             HandleDragUpdate(tempInfo);
784         } else {
785             HandleDragUpdate(info);
786         }
787     }
788     ReportToDragFRCScene(info.GetMainVelocity(), NG::SceneStatus::END);
789     bool isScrollFromTouchPad = info.GetSourceTool() == SourceTool::TOUCHPAD;
790     isDragUpdateStop_ = false;
791     scrollPause_ = false;
792     lastGestureVelocity_ = GetPanDirection() == Axis::NONE ? 0.0 : info.GetMainVelocity();
793     isSlow_ = LessNotEqual(std::abs(lastGestureVelocity_), SLOW_FRICTION_THRESHOLD);
794     SetDragEndPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
795     lastPos_ = GetDragOffset();
796     JankFrameReport::GetInstance().ClearFrameJankFlag(JANK_RUNNING_SCROLL);
797     double mainPosition = Round(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
798     if (!moved_ || isAxisEvent) {
799         LayoutDirectionEst(lastGestureVelocity_, flingVelocityScale_, isScrollFromTouchPad);
800         ResetContinueDragCount();
801         if (GetSnapType() == SnapType::SCROLL_SNAP) {
802             currentPos_ = mainPosition;
803             SnapAnimationOptions snapAnimationOptions = { .animationVelocity = currentVelocity_ };
804             if (startSnapAnimationCallback_ && startSnapAnimationCallback_(snapAnimationOptions)) {
805                 isTouching_ = false;
806                 return;
807             }
808         }
809         HandleScrollEnd(currentVelocity_);
810         currentVelocity_ = 0.f;
811 #ifdef OHOS_PLATFORM
812         if (FrameReport::GetInstance().GetEnable()) {
813             FrameReport::GetInstance().EndListFling();
814         }
815 #endif
816     } else if (canOverScroll_) {
817         LayoutDirectionEst(lastGestureVelocity_, springVelocityScale_, isScrollFromTouchPad);
818         CalcOverScrollVelocity();
819         ResetContinueDragCount();
820         HandleOverScroll(currentVelocity_);
821     } else {
822         LayoutDirectionEst(lastGestureVelocity_, flingVelocityScale_, isScrollFromTouchPad);
823         StartScrollAnimation(mainPosition, currentVelocity_, isScrollFromTouchPad);
824     }
825     ACE_SCOPED_TRACE(
826         "HandleDragEnd, mainPosition:%f, getureDelta:%lf, gestureVelocity:%f, currentVelocity:%f, moved_:%u "
827         "canOverScroll_:%u, id:%d, tag:%s",
828         mainPosition, info.GetMainDelta(), lastGestureVelocity_, currentVelocity_, moved_, canOverScroll_, nodeId_,
829         nodeTag_.c_str());
830     SetDelayedTask();
831     if (dragEndCallback_) {
832         dragEndCallback_();
833     }
834     if (info.GetSourceDevice() != SourceType::CROWN) {
835         isTouching_ = false;
836     }
837     SetDragStartPosition(0.0);
838     SetDragEndPosition(0.0);
839 }
840 
ProcessAxisEndEvent()841 void Scrollable::ProcessAxisEndEvent()
842 {
843     scrollPause_ = false;
844     isTouching_ = false;
845     isDragUpdateStop_ = false;
846     JankFrameReport::GetInstance().ClearFrameJankFlag(JANK_RUNNING_SCROLL);
847 }
848 
ReportToDragFRCScene(double velocity,NG::SceneStatus sceneStatus)849 void Scrollable::ReportToDragFRCScene(double velocity, NG::SceneStatus sceneStatus)
850 {
851     CHECK_NULL_VOID(dragFRCSceneCallback_);
852     dragFRCSceneCallback_(velocity, sceneStatus);
853 }
854 
CalcOverScrollVelocity()855 void Scrollable::CalcOverScrollVelocity()
856 {
857     auto gamma = 0.0f;
858     if (overScrollOffsetCallback_ && continuousSlidingCallback_) {
859         gamma = overScrollOffsetCallback_() / continuousSlidingCallback_();
860     }
861     gamma = GreatOrEqual(gamma, 1.0) ? 1.0f : gamma;
862     currentVelocity_ = currentVelocity_ * exp(-ratio_ * gamma);
863 }
864 
StartScrollAnimation(float mainPosition,float correctVelocity,bool isScrollFromTouchPad)865 void Scrollable::StartScrollAnimation(float mainPosition, float correctVelocity, bool isScrollFromTouchPad)
866 {
867     if (state_ == AnimationState::SPRING) {
868         StopSpringAnimation();
869     }
870     if (!frictionOffsetProperty_) {
871         GetFrictionProperty();
872     }
873     StopSnapController();
874     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "The position of scroll motion is %{public}f, velocity is %{public}f",
875         mainPosition, correctVelocity);
876     double frictionTmp = friction_;
877     if (NearEqual(friction_, -1.0)) {
878         auto defaultFriction = isSlow_ ? SLOW_FRICTION : defaultFriction_;
879         double ret = SystemProperties::GetSrollableFriction();
880         frictionTmp = !NearZero(ret) ? ret : defaultFriction;
881     }
882     float friction = frictionTmp;
883     initVelocity_ = correctVelocity;
884     finalPosition_ = mainPosition + correctVelocity / (friction * -FRICTION_SCALE);
885     if (fixScrollParamCallback_) {
886         fixScrollParamCallback_(mainPosition, initVelocity_, finalPosition_);
887         correctVelocity = initVelocity_;
888         currentVelocity_ = correctVelocity;
889     }
890     currentPos_ = mainPosition;
891     SnapAnimationOptions snapAnimationOptions = {
892         .snapDelta = GetFinalPosition() - mainPosition,
893         .animationVelocity = correctVelocity,
894         .dragDistance = GetDragOffset(),
895     };
896     if (startSnapAnimationCallback_ && startSnapAnimationCallback_(snapAnimationOptions)) {
897         if (GetSnapType() == SnapType::LIST_SNAP) {
898             currentVelocity_ = 0.0;
899         }
900         return;
901     }
902     float threshold = START_FRICTION_VELOCITY_THRESHOLD;
903 #ifdef SUPPORT_DIGITAL_CROWN
904     threshold = isCrownEventDragging_ ? CROWN_START_FRICTION_VELOCITY_THRESHOLD : threshold;
905 #endif
906     if (NearZero(correctVelocity, threshold)) {
907         HandleScrollEnd(correctVelocity);
908         currentVelocity_ = 0.0;
909 #ifdef OHOS_PLATFORM
910         if (FrameReport::GetInstance().GetEnable()) {
911             FrameReport::GetInstance().EndListFling();
912         }
913 #endif
914         return;
915     }
916     TriggerFrictionAnimation(mainPosition, friction, correctVelocity);
917 }
918 
TriggerFrictionAnimation(float mainPosition,float friction,float correctVelocity)919 void Scrollable::TriggerFrictionAnimation(float mainPosition, float friction, float correctVelocity)
920 {
921     // change motion param when list item need to be center of screen on watch
922     FixScrollMotion(mainPosition, correctVelocity);
923     currentVelocity_ = 0.0;
924     lastPosition_ = currentPos_;
925     frictionVelocity_ = initVelocity_;
926     frictionOffsetProperty_->Set(mainPosition);
927     float response = fabs(2 * M_PI / (FRICTION_SCALE * friction));
928     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(response, 1.0f, 0.0f);
929     AnimationOption option;
930     option.SetCurve(curve);
931     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
932     option.SetFinishCallbackType(FinishCallbackType::LOGICALLY);
933     frictionOffsetProperty_->SetThresholdType(ThresholdType::LAYOUT);
934     frictionOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
935     ACE_SCOPED_TRACE("Scrollable friction animation start, start:%f, end:%f, vel:%f, id:%d, tag:%s", mainPosition,
936         finalPosition_, initVelocity_, nodeId_, nodeTag_.c_str());
937     frictionOffsetProperty_->AnimateWithVelocity(
938         option, finalPosition_, initVelocity_, [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
939             ContainerScope scope(id);
940             auto scroll = weak.Upgrade();
941             CHECK_NULL_VOID(scroll);
942             scroll->state_ = AnimationState::IDLE;
943             ACE_SCOPED_TRACE(
944                 "Scrollable friction animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
945             scroll->ProcessScrollMotionStop();
946         });
947     state_ = AnimationState::FRICTION;
948     auto context = context_.Upgrade();
949     CHECK_NULL_VOID(context);
950     context->RequestFrame();
951     lastVsyncTime_ = context->GetVsyncTime();
952 }
953 
SetDelayedTask()954 void Scrollable::SetDelayedTask()
955 {
956     SetContinuousDragStatus(true);
957     auto context = context_.Upgrade();
958     CHECK_NULL_VOID(context);
959     auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
960     task_.Reset([weak = WeakClaim(this)] {
961         auto drag = weak.Upgrade();
962         if (drag) {
963             drag->ResetContinueDragCount();
964             drag->SetContinuousDragStatus(false);
965         }
966     });
967     taskExecutor.PostDelayedTask(task_, DRAG_INTERVAL_TIME, "ArkUIScrollDragInterval");
968 }
969 
MarkNeedFlushAnimationStartTime()970 void Scrollable::MarkNeedFlushAnimationStartTime()
971 {
972     auto context = context_.Upgrade();
973     CHECK_NULL_VOID(context);
974     context->MarkNeedFlushAnimationStartTime();
975 }
976 
ComputeCap(int dragCount)977 double Scrollable::ComputeCap(int dragCount)
978 {
979     if (dragCount < FIRST_THRESHOLD) {
980         return 1.0;
981     }
982     auto cap = ComputeCap(dragCount - 1) + CAP_COEFFICIENT * (dragCount - 1);
983     return cap;
984 }
985 
GetGain(double delta)986 double Scrollable::GetGain(double delta)
987 {
988     auto cap = 1.0;
989     auto gain = 1.0;
990     if (std::abs(delta) < MULTI_FLING_DISTANCE) {
991         ResetContinueDragCount();
992         preGain_ = gain;
993         return gain;
994     }
995     if (!continuousSlidingCallback_) {
996         preGain_ = gain;
997         return gain;
998     }
999     auto screenHeight = continuousSlidingCallback_();
1000     if (delta == 0 || screenHeight == 0) {
1001         preGain_ = gain;
1002         return gain;
1003     }
1004     if (dragCount_ >= FIRST_THRESHOLD && dragCount_ < SECOND_THRESHOLD) {
1005         if (Negative(lastPos_ / delta)) {
1006             ResetContinueDragCount();
1007             preGain_ = gain;
1008             return gain;
1009         }
1010         cap = CAP_COEFFICIENT * (dragCount_ - 1);
1011         gain = (LessNotEqual(cap, std::abs(delta) / screenHeight * (dragCount_ - 1)))
1012                    ? preGain_ + cap
1013                    : preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1);
1014     } else if (dragCount_ >= SECOND_THRESHOLD) {
1015         if (Negative(lastPos_ / delta)) {
1016             ResetContinueDragCount();
1017             preGain_ = gain;
1018             return gain;
1019         }
1020         cap = CAP_FIXED_VALUE;
1021         gain = (LessNotEqual(cap, preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1)))
1022                    ? cap
1023                    : preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1);
1024     }
1025     preGain_ = gain;
1026     return gain;
1027 }
1028 
ExecuteScrollBegin(double & mainDelta)1029 void Scrollable::ExecuteScrollBegin(double& mainDelta)
1030 {
1031     auto context = context_.Upgrade();
1032     if (!scrollBeginCallback_ || !context) {
1033         return;
1034     }
1035 
1036     ScrollInfo scrollInfo;
1037     if (axis_ == Axis::VERTICAL) {
1038         scrollInfo = scrollBeginCallback_(0.0_vp, Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP));
1039         mainDelta = context->NormalizeToPx(scrollInfo.dy);
1040     } else if (axis_ == Axis::HORIZONTAL) {
1041         scrollInfo = scrollBeginCallback_(Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP), 0.0_vp);
1042         mainDelta = context->NormalizeToPx(scrollInfo.dx);
1043     }
1044 }
1045 
GetFrictionVelocityByFinalPosition(float final,float position,float friction,float signum,float threshold)1046 float Scrollable::GetFrictionVelocityByFinalPosition(
1047     float final, float position, float friction, float signum, float threshold)
1048 {
1049     return DEFAULT_THRESHOLD * threshold * signum - (final - position) * friction;
1050 }
1051 
InitFriction(double friction)1052 void Scrollable::InitFriction(double friction)
1053 {
1054     defaultFriction_ =
1055         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
1056     defaultFriction_ =
1057         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : defaultFriction_;
1058     defaultFriction_ =
1059         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_THIRTEEN) ? friction : defaultFriction_;
1060 }
1061 
FixScrollMotion(float position,float initVelocity)1062 void Scrollable::FixScrollMotion(float position, float initVelocity)
1063 {
1064 #ifdef WEARABLE_PRODUCT
1065     float signum = 0.0;
1066     if (!NearZero(initVelocity)) {
1067         signum = GreatNotEqual(initVelocity, 0.0) ? 1.0 : -1.0;
1068     }
1069     if (frictionOffsetProperty_ && needCenterFix_ && watchFixCallback_) {
1070         float finalPosition = watchFixCallback_(GetFinalPosition(), position);
1071         if (!NearEqual(finalPosition, GetFinalPosition(), DISTANCE_EPSILON)) {
1072             float friction = friction_;
1073             float velocity = GetFrictionVelocityByFinalPosition(finalPosition, position, friction, signum);
1074 
1075             // fix again when velocity is less than velocity threshold
1076             if (!NearEqual(finalPosition, GetFinalPosition(), DISTANCE_EPSILON)) {
1077                 velocity = GetFrictionVelocityByFinalPosition(finalPosition, position, friction, signum, 0.0f);
1078             }
1079             initVelocity_ = velocity;
1080             finalPosition_ = mainPosition + initVelocity_ / (friction * -FRICTION_SCALE);
1081         }
1082     }
1083 #endif
1084 }
1085 
StartListSnapAnimation(float predictSnapOffset,float scrollSnapVelocity,bool fromScrollBar)1086 void Scrollable::StartListSnapAnimation(float predictSnapOffset, float scrollSnapVelocity, bool fromScrollBar)
1087 {
1088     endPos_ = currentPos_ + predictSnapOffset;
1089     finalPosition_ = endPos_;
1090     snapAnimationFromScrollBar_ = fromScrollBar;
1091     AnimationOption option;
1092     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
1093     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
1094     option.SetCurve(curve);
1095     if (!snapOffsetProperty_) {
1096         GetSnapProperty();
1097     }
1098     snapOffsetProperty_->Set(currentPos_);
1099     snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
1100     ACE_SCOPED_TRACE("List snap animation start, start:%f, end:%f, vel:%f, id:%d", currentPos_, endPos_,
1101         scrollSnapVelocity, nodeId_);
1102     updateSnapAnimationCount_++;
1103     snapOffsetProperty_->AnimateWithVelocity(
1104         option, endPos_, scrollSnapVelocity, [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1105             ContainerScope scope(id);
1106             auto scroll = weak.Upgrade();
1107             CHECK_NULL_VOID(scroll);
1108             ACE_SCOPED_TRACE("List snap animation finish, id:%d", scroll->nodeId_);
1109             scroll->updateSnapAnimationCount_--;
1110             if (scroll->updateSnapAnimationCount_ == 0) {
1111                 scroll->state_ = AnimationState::IDLE;
1112                 scroll->axisSnapDistance_ = 0.f;
1113                 scroll->ProcessScrollSnapStop();
1114             }
1115             scroll->snapAnimationFromScrollBar_ = false;
1116         });
1117     state_ = AnimationState::SNAP;
1118     auto context = context_.Upgrade();
1119     CHECK_NULL_VOID(context);
1120     lastVsyncTime_ = context->GetVsyncTime();
1121     MarkNeedFlushAnimationStartTime();
1122 }
1123 
StartScrollSnapAnimation(float scrollSnapDelta,float scrollSnapVelocity,bool fromScrollBar)1124 void Scrollable::StartScrollSnapAnimation(float scrollSnapDelta, float scrollSnapVelocity, bool fromScrollBar)
1125 {
1126     TAG_LOGD(AceLogTag::ACE_SCROLLABLE,
1127         "The snap delta of scroll motion is %{public}f, "
1128         "The snap velocity of scroll motion is %{public}f",
1129         scrollSnapDelta, scrollSnapVelocity);
1130     if (NearZero(scrollSnapDelta)) {
1131         return;
1132     }
1133     endPos_ = currentPos_ + scrollSnapDelta;
1134     finalPosition_ = endPos_;
1135     snapAnimationFromScrollBar_ = fromScrollBar;
1136     ACE_SCOPED_TRACE("Scroll snap animation start, start:%f, end:%f, vel:%f, id:%d", currentPos_, endPos_,
1137         scrollSnapVelocity, nodeId_);
1138     AnimationOption option;
1139     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
1140     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
1141     auto minimumAmplitudeRatio = DEFAULT_MINIMUM_AMPLITUDE_PX / std::abs(scrollSnapDelta);
1142     minimumAmplitudeRatio = std::min(minimumAmplitudeRatio, RESPONSIVE_SPRING_AMPLITUDE_RATIO);
1143     if (LessNotEqualCustomPrecision(minimumAmplitudeRatio,
1144         ResponsiveSpringMotion::DEFAULT_RESPONSIVE_SPRING_AMPLITUDE_RATIO)) {
1145         curve->UpdateMinimumAmplitudeRatio(minimumAmplitudeRatio);
1146     }
1147     option.SetCurve(curve);
1148     if (!snapOffsetProperty_) {
1149         GetSnapProperty();
1150     }
1151     snapOffsetProperty_->Set(currentPos_);
1152     snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
1153     updateSnapAnimationCount_++;
1154     snapOffsetProperty_->AnimateWithVelocity(
1155         option, endPos_, scrollSnapVelocity, [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1156             ContainerScope scope(id);
1157             auto scroll = weak.Upgrade();
1158             CHECK_NULL_VOID(scroll);
1159             scroll->updateSnapAnimationCount_--;
1160             if (scroll->updateSnapAnimationCount_ == 0) {
1161                 scroll->state_ = AnimationState::IDLE;
1162                 scroll->nextStep_.reset();
1163                 scroll->axisSnapDistance_ = 0.f;
1164                 scroll->snapDirection_ = SnapDirection::NONE;
1165                 ACE_SCOPED_TRACE("Scroll snap animation finish, id:%d", scroll->nodeId_);
1166                 scroll->ProcessScrollMotionStop();
1167             }
1168             scroll->snapAnimationFromScrollBar_ = false;
1169         });
1170     state_ = AnimationState::SNAP;
1171     auto context = context_.Upgrade();
1172     CHECK_NULL_VOID(context);
1173     lastVsyncTime_ = context->GetVsyncTime();
1174 }
1175 
UpdateScrollSnapStartOffset(double offset)1176 void Scrollable::UpdateScrollSnapStartOffset(double offset)
1177 {
1178     UpdateScrollSnapEndWithOffset(offset);
1179 }
1180 
ProcessListSnapMotion(double position)1181 void Scrollable::ProcessListSnapMotion(double position)
1182 {
1183     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "Current Pos is %{public}f, position is %{public}f", currentPos_, position);
1184     currentVelocity_ = snapVelocity_;
1185     auto source = snapAnimationFromScrollBar_ ? SCROLL_FROM_BAR_FLING : SCROLL_FROM_ANIMATION;
1186     if (NearEqual(currentPos_, position)) {
1187         UpdateScrollPosition(0.0, source);
1188     } else {
1189         auto mainDelta = position - currentPos_;
1190         HandleScroll(mainDelta, source, NestedState::GESTURE);
1191         if (!moved_ && state_ == AnimationState::SNAP) {
1192             StopSnapAnimation();
1193         }
1194     }
1195     if (LessOrEqual(std::abs(currentPos_ - position), 1)) {
1196         // trace stop at OnScrollStop
1197         AceAsyncTraceBeginCommercial(
1198             nodeId_, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") + nodeTag_).c_str());
1199     }
1200     currentPos_ = position;
1201     if (canOverScroll_ && state_ == AnimationState::SNAP) {
1202         if (source != SCROLL_FROM_BAR_FLING) {
1203             scrollPause_ = true;
1204             skipRestartSpring_ = true;
1205             MarkNeedFlushAnimationStartTime();
1206         }
1207         StopSnapAnimation();
1208     }
1209 }
1210 
ProcessScrollSnapStop()1211 void Scrollable::ProcessScrollSnapStop()
1212 {
1213     if (scrollPause_) {
1214         scrollPause_ = false;
1215         HandleOverScroll(currentVelocity_);
1216     } else {
1217         OnAnimateStop();
1218     }
1219 }
1220 
OnAnimateStop()1221 void Scrollable::OnAnimateStop()
1222 {
1223     HandleScrollEnd(std::nullopt);
1224     currentVelocity_ = 0.0;
1225     if (isTouching_ || isDragUpdateStop_) {
1226         return;
1227     }
1228     moved_ = false;
1229 #ifdef OHOS_PLATFORM
1230     if (FrameReport::GetInstance().GetEnable()) {
1231         FrameReport::GetInstance().EndListFling();
1232     }
1233 #endif
1234 #if !defined(PREVIEW)
1235     LayoutInspector::SupportInspector();
1236 #endif
1237 }
1238 
StartSpringMotion(double mainPosition,double mainVelocity,const ExtentPair & extent,const ExtentPair & initExtent)1239 void Scrollable::StartSpringMotion(
1240     double mainPosition, double mainVelocity, const ExtentPair& extent, const ExtentPair& initExtent)
1241 {
1242     TAG_LOGD(AceLogTag::ACE_SCROLLABLE,
1243         "position is %{public}f, mainVelocity is %{public}f, minExtent is "
1244         "%{public}f, maxExtent is %{public}f, initMinExtent is %{public}f, initMaxExtent is %{public}f",
1245         mainPosition, mainVelocity, extent.Leading(), extent.Trailing(), initExtent.Leading(), initExtent.Trailing());
1246     if (state_ == AnimationState::SPRING || (skipRestartSpring_ && NearEqual(mainVelocity, 0.0f, 0.001f))) {
1247         return;
1248     }
1249     currentPos_ = mainPosition;
1250     if (mainPosition > initExtent.Trailing() || NearEqual(mainPosition, initExtent.Trailing(), 0.01f)) {
1251         finalPosition_ = extent.Trailing();
1252     } else if (mainPosition < initExtent.Leading() || NearEqual(mainPosition, initExtent.Leading(), 0.01f)) {
1253         finalPosition_ = extent.Leading();
1254     } else {
1255         return;
1256     }
1257 
1258     if (!springOffsetProperty_) {
1259         GetSpringProperty();
1260     }
1261     springAnimationCount_++;
1262     springOffsetProperty_->Set(mainPosition);
1263     AnimationOption option;
1264     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(springResponse_, DEFAULT_SPRING_DAMP, 0.0f);
1265     option.SetCurve(curve);
1266     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
1267     springOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
1268     ACE_SCOPED_TRACE("Scrollable spring animation start, start:%f, end:%f, vel:%f, id:%d, tag:%s", mainPosition,
1269         finalPosition_, mainVelocity, nodeId_, nodeTag_.c_str());
1270     lastVsyncTime_ = static_cast<uint64_t>(GetSysTimestamp());
1271     springOffsetProperty_->AnimateWithVelocity(
1272         option, finalPosition_, mainVelocity, [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1273             ContainerScope scope(id);
1274             auto scroll = weak.Upgrade();
1275             CHECK_NULL_VOID(scroll);
1276             scroll->springAnimationCount_--;
1277             ACE_SCOPED_TRACE(
1278                 "Scrollable spring animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
1279             // avoid current animation being interrupted by the prev animation's finish callback
1280             // and triggering onScrollStop when spring animation turns to friction animation.
1281             if (scroll->springAnimationCount_ > 0 || scroll->scrollPause_) {
1282                 scroll->scrollPause_ = false;
1283                 return;
1284             }
1285             scroll->state_ = AnimationState::IDLE;
1286             scroll->currentVelocity_ = 0.0;
1287             scroll->OnAnimateStop();
1288         });
1289     state_ = AnimationState::SPRING;
1290     skipRestartSpring_ = false;
1291     auto context = context_.Upgrade();
1292     CHECK_NULL_VOID(context);
1293     context->RequestFrame();
1294 }
1295 
UpdateSpringMotion(double mainPosition,const ExtentPair & extent,const ExtentPair & initExtent)1296 void Scrollable::UpdateSpringMotion(double mainPosition, const ExtentPair& extent, const ExtentPair& initExtent)
1297 {
1298     TAG_LOGD(AceLogTag::ACE_SCROLLABLE,
1299         "position is %{public}f, minExtent is "
1300         "%{public}f, maxExtent is %{public}f, initMinExtent is %{public}f, initMaxExtent is %{public}f",
1301         mainPosition, extent.Leading(), extent.Trailing(), initExtent.Leading(), initExtent.Trailing());
1302     if (state_ != AnimationState::SPRING || !springOffsetProperty_) {
1303         return;
1304     }
1305     float finalPosition = 0.0f;
1306     if (mainPosition > initExtent.Trailing() || NearEqual(mainPosition, initExtent.Trailing())) {
1307         finalPosition = extent.Trailing();
1308     } else if (mainPosition < initExtent.Leading() || NearEqual(mainPosition, initExtent.Leading())) {
1309         finalPosition = extent.Leading();
1310     } else {
1311         return;
1312     }
1313 
1314     finalPosition = finalPosition_ + (finalPosition - mainPosition) - (finalPosition_ - currentPos_);
1315     if (NearEqual(finalPosition, finalPosition_, SPRING_ACCURACY)) {
1316         return;
1317     }
1318     finalPosition_ = finalPosition;
1319     springAnimationCount_++;
1320     AnimationOption option;
1321     auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
1322     option.SetCurve(curve);
1323     option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
1324     springOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
1325     ACE_SCOPED_TRACE("Scrollable spring animation update, start:%f, end:%f, id:%d, tag:%s", mainPosition,
1326         finalPosition_, nodeId_, nodeTag_.c_str());
1327     AnimationUtils::StartAnimation(
1328         option,
1329         [weak = AceType::WeakClaim(this)]() {
1330             auto scroll = weak.Upgrade();
1331             CHECK_NULL_VOID(scroll);
1332             scroll->springOffsetProperty_->Set(scroll->finalPosition_);
1333             scroll->state_ = AnimationState::SPRING;
1334         },
1335         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1336             ContainerScope scope(id);
1337             auto scroll = weak.Upgrade();
1338             CHECK_NULL_VOID(scroll);
1339             scroll->springAnimationCount_--;
1340             // avoid current animation being interrupted by the prev animation's finish callback
1341             if (scroll->springAnimationCount_ > 0) {
1342                 return;
1343             }
1344             ACE_SCOPED_TRACE(
1345                 "Scrollable updated spring animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
1346             scroll->state_ = AnimationState::IDLE;
1347             scroll->currentVelocity_ = 0.0;
1348             scroll->OnAnimateStop();
1349         });
1350     state_ = AnimationState::SPRING;
1351     skipRestartSpring_ = false;
1352 }
1353 
ProcessScrollMotionStop()1354 void Scrollable::ProcessScrollMotionStop()
1355 {
1356     if (needScrollSnapChange_ && startSnapAnimationCallback_ && frictionOffsetProperty_) {
1357         needScrollSnapChange_ = false;
1358         SnapAnimationOptions snapAnimationOptions = {
1359             .snapDelta = GetFinalPosition() - currentPos_,
1360             .animationVelocity = currentVelocity_,
1361         };
1362         CHECK_NULL_VOID(!startSnapAnimationCallback_(snapAnimationOptions));
1363     }
1364     // spring effect special process
1365     if (scrollPause_) {
1366         scrollPause_ = false;
1367         state_ = AnimationState::TRANSITION;
1368         HandleOverScroll(currentVelocity_);
1369         if (state_ == AnimationState::TRANSITION) {
1370             // didn't trigger spring animation
1371             state_ = AnimationState::IDLE;
1372         }
1373         return;
1374     }
1375 
1376     if (isDragUpdateStop_) {
1377         return;
1378     }
1379     moved_ = false;
1380     HandleScrollEnd(std::nullopt);
1381 #ifdef OHOS_PLATFORM
1382     if (FrameReport::GetInstance().GetEnable()) {
1383         FrameReport::GetInstance().EndListFling();
1384     }
1385 #endif
1386     currentVelocity_ = 0.0;
1387 #if !defined(PREVIEW)
1388     LayoutInspector::SupportInspector();
1389 #endif
1390 }
1391 
ProcessSpringMotion(double position)1392 void Scrollable::ProcessSpringMotion(double position)
1393 {
1394     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "Current Pos is %{public}f, position is %{public}f", currentPos_, position);
1395     auto context = context_.Upgrade();
1396     CHECK_NULL_VOID(context);
1397     uint64_t currentVsync = context->GetVsyncTime();
1398     uint64_t diff = currentVsync - lastVsyncTime_;
1399     if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1400         currentVelocity_ = (position - currentPos_) / diff * MILLOS_PER_NANO_SECONDS;
1401     }
1402     lastVsyncTime_ = currentVsync;
1403     if (LessOrEqual(std::abs(currentPos_ - position), 1)) {
1404         // trace stop at OnScrollStop
1405         if (!isFadingAway_) {
1406             AceAsyncTraceBeginCommercial(
1407                 nodeId_, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") + nodeTag_).c_str());
1408         } else {
1409             ACE_SCOPED_TRACE("Spring to same position");
1410         }
1411     }
1412     auto distance = currentPos_ - finalPosition_;
1413     auto nextDistance = position - finalPosition_;
1414     isFadingAway_ = GreatNotEqual(std::abs(nextDistance), std::abs(distance));
1415     auto delta = position - currentPos_;
1416     if (distance * nextDistance < 0) {
1417         double currentVelocity = currentVelocity_;
1418         scrollPause_ = true;
1419         MarkNeedFlushAnimationStartTime();
1420         StopSpringAnimation();
1421         ACE_SCOPED_TRACE("change direction in spring animation and start fling animation, distance:%f, "
1422                          "nextDistance:%f, nodeId:%d, tag:%s",
1423             distance, nextDistance, nodeId_, nodeTag_.c_str());
1424         // only handle offsets that are out of bounds
1425         delta = finalPosition_ - currentPos_;
1426         moved_ = UpdateScrollPosition(delta, SCROLL_FROM_ANIMATION_SPRING);
1427         // remainVelocityCallback_ will pass the velocity to the child component
1428         if (!remainVelocityCallback_ || !remainVelocityCallback_(currentVelocity)) {
1429             StartScrollAnimation(position, currentVelocity);
1430         }
1431     } else {
1432         moved_ = UpdateScrollPosition(delta, SCROLL_FROM_ANIMATION_SPRING);
1433     }
1434     if (!moved_) {
1435         StopSpringAnimation();
1436     }
1437     currentPos_ = position;
1438 }
1439 
CalcNextStep(double position,double mainDelta)1440 double Scrollable::CalcNextStep(double position, double mainDelta)
1441 {
1442     auto finalDelta = finalPosition_ - currentPos_;
1443     if (GreatNotEqual(std::abs(finalDelta), SCROLL_SNAP_MIN_STEP_THRESHOLD)) {
1444         return mainDelta;
1445     }
1446     if (LessOrEqual(std::abs(finalDelta), SCROLL_SNAP_MIN_STEP)) {
1447         return finalDelta;
1448     }
1449     if (nextStep_.has_value()) {
1450         return nextStep_.value();
1451     }
1452     if (LessOrEqual(std::abs(mainDelta), SCROLL_SNAP_MIN_STEP)) {
1453         nextStep_ = Positive(mainDelta) ? SCROLL_SNAP_MIN_STEP : -SCROLL_SNAP_MIN_STEP;
1454         mainDelta = nextStep_.value();
1455     }
1456     return mainDelta;
1457 }
1458 
ProcessScrollMotion(double position,int32_t source)1459 void Scrollable::ProcessScrollMotion(double position, int32_t source)
1460 {
1461     currentVelocity_ = state_ == AnimationState::SNAP ? snapVelocity_ : frictionVelocity_;
1462     auto mainDelta = position - currentPos_;
1463 #ifdef SUPPORT_DIGITAL_CROWN
1464     if (state_ == AnimationState::SNAP) {
1465         mainDelta = CalcNextStep(position, mainDelta);
1466         position = currentPos_ + mainDelta;
1467     }
1468 #endif
1469     if (needScrollSnapToSideCallback_) {
1470         needScrollSnapChange_ = needScrollSnapToSideCallback_(mainDelta);
1471     }
1472     TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "position is %{public}f, currentVelocity_ is %{public}f, "
1473         "needScrollSnapChange_ is %{public}u", position, currentVelocity_, needScrollSnapChange_);
1474     if (LessOrEqual(std::abs(mainDelta), 1)) {
1475         // trace stop at OnScrollStop
1476         AceAsyncTraceBeginCommercial(
1477             nodeId_, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") + nodeTag_).c_str());
1478     }
1479     // UpdateScrollPosition return false, means reach to scroll limit.
1480     source = snapAnimationFromScrollBar_ && state_ == AnimationState::SNAP ? SCROLL_FROM_BAR_FLING : source;
1481     HandleScroll(mainDelta, source, NestedState::GESTURE);
1482     if (!moved_) {
1483         ResetContinueDragCount();
1484         StopFrictionAnimation();
1485     }
1486     currentPos_ = position;
1487 #ifdef SUPPORT_DIGITAL_CROWN
1488     if (state_ == AnimationState::SNAP && NearEqual(currentPos_, finalPosition_)) {
1489         StopSnapAnimation();
1490     }
1491 #endif
1492     // spring effect special process
1493     if ((canOverScroll_ && source != SCROLL_FROM_AXIS) || needScrollSnapChange_) {
1494         ACE_SCOPED_TRACE("scrollPause set true to stop ProcessScrollMotion, canOverScroll:%u, needScrollSnapChange:%u, "
1495                          "nodeId:%d, tag:%s",
1496             canOverScroll_, needScrollSnapChange_, nodeId_, nodeTag_.c_str());
1497         if (source != SCROLL_FROM_BAR_FLING) {
1498             scrollPause_ = true;
1499             skipRestartSpring_ = true;
1500             MarkNeedFlushAnimationStartTime();
1501         }
1502         ResetContinueDragCount();
1503         StopFrictionAnimation();
1504         StopSnapAnimation();
1505     }
1506 }
1507 
UpdateScrollPosition(const double offset,int32_t source) const1508 bool Scrollable::UpdateScrollPosition(const double offset, int32_t source) const
1509 {
1510     bool ret = true;
1511     if (callback_) {
1512         ret = callback_(offset, source);
1513     }
1514     return ret;
1515 }
1516 
ProcessScrollOverCallback(double velocity)1517 void Scrollable::ProcessScrollOverCallback(double velocity)
1518 {
1519     if (outBoundaryCallback_ && !outBoundaryCallback_() && !canOverScroll_) {
1520         return;
1521     }
1522     // In the case of chain animation enabled, you need to switch the control point first,
1523     // and then correct the offset value in notification process
1524     if (notifyScrollOverCallback_) {
1525         notifyScrollOverCallback_(velocity);
1526     }
1527     // then use corrected offset to make scroll motion.
1528     if (scrollOverCallback_) {
1529         scrollOverCallback_(velocity);
1530     }
1531 }
1532 
HandleOverScroll(double velocity)1533 bool Scrollable::HandleOverScroll(double velocity)
1534 {
1535     if (!overScrollCallback_) {
1536         if (edgeEffect_ == EdgeEffect::SPRING) {
1537             ProcessScrollOverCallback(velocity);
1538             return true;
1539         }
1540         if (scrollEndCallback_) {
1541             scrollEndCallback_();
1542         }
1543         return false;
1544     }
1545     // call NestableScrollContainer::HandleOverScroll
1546     return overScrollCallback_(velocity);
1547 }
1548 
SetSlipFactor(double SlipFactor)1549 void Scrollable::SetSlipFactor(double SlipFactor)
1550 {
1551     slipFactor_ = std::clamp(SlipFactor, -ADJUSTABLE_VELOCITY, ADJUSTABLE_VELOCITY);
1552 }
1553 
UpdateScrollSnapEndWithOffset(double offset)1554 void Scrollable::UpdateScrollSnapEndWithOffset(double offset)
1555 {
1556     if (state_ == AnimationState::SNAP) {
1557         MarkNeedFlushAnimationStartTime();
1558         AnimationOption option;
1559         option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
1560         auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
1561         option.SetCurve(curve);
1562         if (!snapOffsetProperty_) {
1563             GetSnapProperty();
1564         }
1565         updateSnapAnimationCount_++;
1566         endPos_ -= offset;
1567         snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
1568         AnimationUtils::StartAnimation(
1569             option,
1570             [weak = AceType::WeakClaim(this)]() {
1571                 auto scroll = weak.Upgrade();
1572                 CHECK_NULL_VOID(scroll);
1573                 scroll->snapOffsetProperty_->Set(scroll->endPos_);
1574                 scroll->state_ = AnimationState::SNAP;
1575             },
1576             [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1577                 ContainerScope scope(id);
1578                 auto scroll = weak.Upgrade();
1579                 CHECK_NULL_VOID(scroll);
1580                 scroll->updateSnapAnimationCount_--;
1581                 // avoid current animation being interrupted by the prev animation's finish callback
1582                 if (scroll->updateSnapAnimationCount_ == 0) {
1583                     scroll->snapDirection_ = SnapDirection::NONE;
1584                     scroll->state_ = AnimationState::IDLE;
1585                     scroll->ProcessScrollSnapStop();
1586                 }
1587             });
1588         state_ = AnimationState::SNAP;
1589     }
1590 }
1591 
GetPredictSnapOffset() const1592 std::optional<float> Scrollable::GetPredictSnapOffset() const
1593 {
1594     if (state_ == AnimationState::SNAP) {
1595         return endPos_ - currentPos_;
1596     }
1597     return std::nullopt;
1598 }
1599 
AttachAnimatableProperty(const RefPtr<NodeAnimatablePropertyFloat> & property)1600 void Scrollable::AttachAnimatableProperty(const RefPtr<NodeAnimatablePropertyFloat>& property)
1601 {
1602     auto host = weakHost_.Upgrade();
1603     CHECK_NULL_VOID(host);
1604     auto renderContext = host->GetRenderContext();
1605     CHECK_NULL_VOID(renderContext);
1606     renderContext->AttachNodeAnimatableProperty(property);
1607 }
1608 
GetFrictionProperty()1609 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetFrictionProperty()
1610 {
1611     auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1612         auto scroll = weak.Upgrade();
1613         CHECK_NULL_VOID(scroll);
1614         if (scroll->state_ != AnimationState::FRICTION || scroll->isTouching_) {
1615             return;
1616         }
1617         scroll->ProcessScrollMotion(position);
1618         if (NearEqual(scroll->finalPosition_, position, 1.0)) {
1619             scroll->StopFrictionAnimation();
1620         }
1621         auto context = scroll->context_.Upgrade();
1622         CHECK_NULL_VOID(context);
1623         uint64_t currentVsync = context->GetVsyncTime();
1624         uint64_t diff = currentVsync - scroll->lastVsyncTime_;
1625         if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1626             scroll->frictionVelocity_ = (position - scroll->lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1627             if (NearZero(scroll->frictionVelocity_, FRICTION_VELOCITY_THRESHOLD)) {
1628                 scroll->StopFrictionAnimation();
1629                 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
1630             }
1631         }
1632         scroll->lastVsyncTime_ = currentVsync;
1633         scroll->lastPosition_ = position;
1634     };
1635     frictionOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1636     AttachAnimatableProperty(frictionOffsetProperty_);
1637     return frictionOffsetProperty_;
1638 }
1639 
GetSpringProperty()1640 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetSpringProperty()
1641 {
1642     auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1643         auto scroll = weak.Upgrade();
1644         CHECK_NULL_VOID(scroll);
1645         if (scroll->state_ != AnimationState::SPRING) {
1646             return;
1647         }
1648         if (!NearEqual(scroll->finalPosition_, position, SPRING_ACCURACY)) {
1649             scroll->ProcessSpringMotion(position);
1650             return;
1651         }
1652         /*
1653          * In order to prevent accumulation errors, the current position is re obtained to ensure that
1654          * the last frame can accurately stop at the top and bottom positions.
1655          */
1656         if (scroll->currentPositionCallback_) {
1657             double currPos = scroll->currentPositionCallback_();
1658             if (NearEqual(currPos, scroll->currentPos_, 0.5)) {
1659                 scroll->currentPos_ = currPos;
1660             }
1661         }
1662         scroll->StopSpringAnimation(true);
1663     };
1664     springOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1665     AttachAnimatableProperty(springOffsetProperty_);
1666     return springOffsetProperty_;
1667 }
1668 
GetSnapProperty()1669 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetSnapProperty()
1670 {
1671     auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1672         auto scroll = weak.Upgrade();
1673         CHECK_NULL_VOID(scroll);
1674         if (scroll->state_ != AnimationState::SNAP) {
1675             return;
1676         }
1677         auto context = scroll->context_.Upgrade();
1678         CHECK_NULL_VOID(context);
1679         uint64_t currentVsync = context->GetVsyncTime();
1680         uint64_t diff = currentVsync - scroll->lastVsyncTime_;
1681         if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1682             scroll->snapVelocity_ = (position - scroll->currentPos_) / diff * MILLOS_PER_NANO_SECONDS;
1683         }
1684         scroll->lastVsyncTime_ = currentVsync;
1685 
1686         if (NearEqual(scroll->endPos_, position, SPRING_ACCURACY)) {
1687             if (scroll->GetSnapType() == SnapType::LIST_SNAP) {
1688                 scroll->ProcessListSnapMotion(scroll->endPos_);
1689             } else if (scroll->GetSnapType() == SnapType::SCROLL_SNAP) {
1690                 scroll->ProcessScrollMotion(scroll->endPos_);
1691             }
1692             scroll->StopSnapAnimation();
1693         } else {
1694             if (scroll->GetSnapType() == SnapType::LIST_SNAP) {
1695                 scroll->ProcessListSnapMotion(position);
1696             } else if (scroll->GetSnapType() == SnapType::SCROLL_SNAP) {
1697                 scroll->ProcessScrollMotion(position);
1698             }
1699         }
1700     };
1701     snapOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1702     AttachAnimatableProperty(snapOffsetProperty_);
1703     return snapOffsetProperty_;
1704 }
1705 
StopFrictionAnimation()1706 void Scrollable::StopFrictionAnimation()
1707 {
1708     if (state_ == AnimationState::FRICTION) {
1709         ACE_SCOPED_TRACE("StopFrictionAnimation, id:%d, tag:%s", nodeId_, nodeTag_.c_str());
1710         state_ = AnimationState::IDLE;
1711         CHECK_NULL_VOID(frictionOffsetProperty_);
1712         AnimationOption option;
1713         option.SetCurve(Curves::EASE);
1714         option.SetDuration(0);
1715         AnimationUtils::StartAnimation(
1716             option,
1717             [weak = AceType::WeakClaim(this)]() {
1718                 auto scroll = weak.Upgrade();
1719                 CHECK_NULL_VOID(scroll);
1720                 scroll->frictionOffsetProperty_->Set(scroll->currentPos_);
1721             },
1722             nullptr);
1723     }
1724 }
1725 
StopSpringAnimation(bool reachFinalPosition)1726 void Scrollable::StopSpringAnimation(bool reachFinalPosition)
1727 {
1728     if (state_ == AnimationState::SPRING) {
1729         ACE_SCOPED_TRACE(
1730             "StopSpringAnimation, reachFinalPosition:%u, id:%d, tag:%s", reachFinalPosition, nodeId_, nodeTag_.c_str());
1731         state_ = AnimationState::IDLE;
1732         isFadingAway_ = false;
1733         CHECK_NULL_VOID(springOffsetProperty_);
1734         AnimationOption option;
1735         option.SetCurve(Curves::EASE);
1736         option.SetDuration(0);
1737         AnimationUtils::StartAnimation(
1738             option,
1739             [weak = AceType::WeakClaim(this), reachFinalPosition]() {
1740                 auto scroll = weak.Upgrade();
1741                 CHECK_NULL_VOID(scroll);
1742                 if (reachFinalPosition) {
1743                     // ensure that the spring animation is restored to its final position.
1744                     scroll->ProcessSpringMotion(scroll->finalPosition_);
1745                     // use non final position to stop animation, otherwise the animation cannot be stoped.
1746                     scroll->springOffsetProperty_->Set(scroll->finalPosition_ - 1.f);
1747                 } else {
1748                     // avoid top edge spring can not stop
1749                     scroll->springOffsetProperty_->Set(scroll->currentPos_);
1750                 }
1751             },
1752             nullptr);
1753     }
1754     currentVelocity_ = 0.0;
1755 }
1756 
StopSnapAnimation()1757 void Scrollable::StopSnapAnimation()
1758 {
1759     if (state_ == AnimationState::SNAP) {
1760         ACE_SCOPED_TRACE("StopSnapAnimation, animation state:%d, id:%d, tag:%s", state_, nodeId_, nodeTag_.c_str());
1761         state_ = AnimationState::IDLE;
1762         nextStep_.reset();
1763         CHECK_NULL_VOID(snapOffsetProperty_);
1764         AnimationOption option;
1765         option.SetCurve(Curves::EASE);
1766         option.SetDuration(0);
1767         AnimationUtils::StartAnimation(
1768             option,
1769             [weak = AceType::WeakClaim(this)]() {
1770                 auto scroll = weak.Upgrade();
1771                 CHECK_NULL_VOID(scroll);
1772                 auto position = NearEqual(scroll->currentPos_, scroll->finalPosition_) ?
1773                     scroll->finalPosition_ - 1.f : scroll->currentPos_;
1774                 scroll->snapOffsetProperty_->Set(position);
1775             },
1776             nullptr);
1777     }
1778 }
1779 
StopAxisAnimation()1780 void Scrollable::StopAxisAnimation()
1781 {
1782     CHECK_NULL_VOID(axisAnimator_);
1783     axisAnimator_->StopAxisAnimation();
1784 }
1785 
IsMouseWheelScroll(const GestureEvent & info)1786 inline bool Scrollable::IsMouseWheelScroll(const GestureEvent& info)
1787 {
1788     return info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() != SourceTool::TOUCHPAD;
1789 }
1790 
OnCollectTouchTarget(TouchTestResult & result,const RefPtr<FrameNode> & frameNode,const RefPtr<TargetComponent> & targetComponent,ResponseLinkResult & responseLinkResult)1791 void Scrollable::OnCollectTouchTarget(TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
1792     const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult)
1793 {
1794     if (panRecognizerNG_) {
1795         panRecognizerNG_->SetNodeId(frameNode->GetId());
1796         panRecognizerNG_->AttachFrameNode(frameNode);
1797         panRecognizerNG_->SetTargetComponent(targetComponent);
1798         panRecognizerNG_->SetIsSystemGesture(true);
1799         panRecognizerNG_->SetRecognizerType(GestureTypeName::PAN_GESTURE);
1800         result.emplace_back(panRecognizerNG_);
1801         responseLinkResult.emplace_back(panRecognizerNG_);
1802     }
1803 }
1804 
SetMaxFlingVelocity(double max)1805 void Scrollable::SetMaxFlingVelocity(double max)
1806 {
1807     double density = PipelineBase::GetCurrentDensity();
1808     maxFlingVelocity_ = max * density;
1809 }
1810 } // namespace OHOS::Ace::NG
1811