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