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