1 /*
2 * Copyright (c) 2021 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/scroll/scrollable.h"
17
18 #include "base/log/jank_frame_report.h"
19 #include "base/ressched/ressched_report.h"
20 #include "core/common/layout_inspector.h"
21
22 namespace OHOS::Ace {
23 namespace {
24
25 constexpr float SPRING_SCROLL_MASS = 1.0f;
26 constexpr float SPRING_SCROLL_STIFFNESS = 228.0f;
27 constexpr float SPRING_SCROLL_DAMPING = 30.0f;
28 constexpr double CAP_COEFFICIENT = 0.45;
29 constexpr int32_t FIRST_THRESHOLD = 5;
30 constexpr int32_t SECOND_THRESHOLD = 10;
31 constexpr double CAP_FIXED_VALUE = 16.0;
32 constexpr uint32_t DRAG_INTERVAL_TIME = 900;
33 const RefPtr<SpringProperty> DEFAULT_OVER_SPRING_PROPERTY =
34 AceType::MakeRefPtr<SpringProperty>(SPRING_SCROLL_MASS, SPRING_SCROLL_STIFFNESS, SPRING_SCROLL_DAMPING);
35 #ifndef WEARABLE_PRODUCT
36 constexpr double FRICTION = 0.6;
37 constexpr double VELOCITY_SCALE = 1.0;
38 constexpr double ADJUSTABLE_VELOCITY = 3000.0;
39 #else
40 constexpr double DISTANCE_EPSILON = 1.0;
41 constexpr double FRICTION = 0.9;
42 constexpr double VELOCITY_SCALE = 0.8;
43 constexpr double ADJUSTABLE_VELOCITY = 0.0;
44 #endif
45
46 #ifdef OHOS_PLATFORM
47 constexpr int64_t INCREASE_CPU_TIME_ONCE = 4000000000; // 4s(unit: ns)
48 #endif
49
50 } // namespace
51
52 // Static Functions.
53 double Scrollable::sFriction_ = FRICTION;
54 double Scrollable::sVelocityScale_ = VELOCITY_SCALE;
55
SetVelocityScale(double sVelocityScale)56 void Scrollable::SetVelocityScale(double sVelocityScale)
57 {
58 if (LessOrEqual(sVelocityScale, 0.0)) {
59 return;
60 }
61 sVelocityScale_ = sVelocityScale;
62 }
63
SetFriction(double sFriction)64 void Scrollable::SetFriction(double sFriction)
65 {
66 if (LessOrEqual(sFriction, 0.0)) {
67 return;
68 }
69 sFriction_ = sFriction;
70 }
71
~Scrollable()72 Scrollable::~Scrollable()
73 {
74 // If animation still runs, force stop it.
75 controller_->Stop();
76 springController_->Stop();
77 scrollSnapController_->Stop();
78 }
79
OnFlushTouchEventsBegin()80 void Scrollable::OnFlushTouchEventsBegin()
81 {
82 if (panRecognizer_) {
83 panRecognizer_->OnFlushTouchEventsBegin();
84 }
85 }
86
OnFlushTouchEventsEnd()87 void Scrollable::OnFlushTouchEventsEnd()
88 {
89 if (panRecognizer_) {
90 panRecognizer_->OnFlushTouchEventsEnd();
91 }
92 }
93
Initialize(const WeakPtr<PipelineBase> & context)94 void Scrollable::Initialize(const WeakPtr<PipelineBase>& context)
95 {
96 context_ = context;
97 PanDirection panDirection;
98 if (axis_ == Axis::VERTICAL) {
99 panDirection.type = PanDirection::VERTICAL;
100 } else {
101 panDirection.type = PanDirection::HORIZONTAL;
102 }
103
104 auto actionStart = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
105 auto scroll = weakScroll.Upgrade();
106 if (scroll) {
107 // Send event to accessibility when scroll start.
108 auto context = scroll->GetContext().Upgrade();
109 if (context) {
110 AccessibilityEvent scrollEvent;
111 scrollEvent.nodeId = scroll->nodeId_;
112 scrollEvent.eventType = "scrollstart";
113 context->SendEventToAccessibility(scrollEvent);
114 }
115 scroll->isDragging_ = true;
116 scroll->HandleDragStart(info);
117 }
118 };
119
120 auto actionUpdate = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
121 auto scroll = weakScroll.Upgrade();
122 if (scroll) {
123 scroll->HandleDragUpdate(info);
124 }
125 };
126
127 auto actionEnd = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
128 auto scroll = weakScroll.Upgrade();
129 if (scroll) {
130 scroll->HandleDragEnd(info);
131 // Send event to accessibility when scroll stop.
132 auto context = scroll->GetContext().Upgrade();
133 if (context && scroll->IsStopped()) {
134 AccessibilityEvent scrollEvent;
135 scrollEvent.nodeId = scroll->nodeId_;
136 scrollEvent.eventType = "scrollend";
137 context->SendEventToAccessibility(scrollEvent);
138 }
139 if (scroll->actionEnd_) {
140 auto gestureEvent = info;
141 scroll->actionEnd_(gestureEvent);
142 }
143 scroll->isDragging_ = false;
144 }
145 };
146
147 auto actionCancel = [weakScroll = AceType::WeakClaim(this)]() {
148 auto scroll = weakScroll.Upgrade();
149 if (!scroll) {
150 return;
151 }
152 if (scroll->dragCancelCallback_) {
153 scroll->dragCancelCallback_();
154 }
155 scroll->isDragging_ = false;
156 };
157
158 if (Container::IsCurrentUseNewPipeline()) {
159 panRecognizerNG_ = AceType::MakeRefPtr<NG::PanRecognizer>(
160 DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
161 panRecognizerNG_->SetIsAllowMouse(false);
162 panRecognizerNG_->SetOnActionStart(actionStart);
163 panRecognizerNG_->SetOnActionUpdate(actionUpdate);
164 panRecognizerNG_->SetOnActionEnd(actionEnd);
165 panRecognizerNG_->SetOnActionCancel(actionCancel);
166 } else {
167 panRecognizer_ = AceType::MakeRefPtr<PanRecognizer>(
168 context, DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
169 panRecognizer_->SetOnActionStart(actionStart);
170 panRecognizer_->SetOnActionUpdate(actionUpdate);
171 panRecognizer_->SetOnActionEnd(actionEnd);
172 panRecognizer_->SetOnActionCancel(actionCancel);
173 }
174
175 // use RawRecognizer to receive next touch down event to stop animation.
176 rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
177
178 rawRecognizer_->SetOnTouchDown([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
179 auto scroll = weakScroll.Upgrade();
180 if (scroll) {
181 scroll->HandleTouchDown();
182 }
183 });
184 rawRecognizer_->SetOnTouchUp([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
185 auto scroll = weakScroll.Upgrade();
186 if (scroll) {
187 scroll->HandleTouchUp();
188 }
189 });
190 rawRecognizer_->SetOnTouchCancel([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
191 auto scroll = weakScroll.Upgrade();
192 if (scroll) {
193 scroll->HandleTouchCancel();
194 }
195 });
196
197 controller_ = CREATE_ANIMATOR(context);
198 springController_ = CREATE_ANIMATOR(context);
199 scrollSnapController_ = CREATE_ANIMATOR(context);
200 snapController_ = CREATE_ANIMATOR(context);
201 snapController_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
202 auto scroll = weakScroll.Upgrade();
203 CHECK_NULL_VOID(scroll);
204 scroll->ProcessScrollMotionStop();
205 // Send event to accessibility when scroll stop.
206 auto context = scroll->GetContext().Upgrade();
207 CHECK_NULL_VOID(context && scroll->Idle());
208 AccessibilityEvent scrollEvent;
209 scrollEvent.nodeId = scroll->nodeId_;
210 scrollEvent.eventType = "scrollend";
211 context->SendEventToAccessibility(scrollEvent);
212 });
213
214 spring_ = GetDefaultOverSpringProperty();
215 available_ = true;
216 }
217
SetAxis(Axis axis)218 void Scrollable::SetAxis(Axis axis)
219 {
220 axis_ = axis;
221 PanDirection panDirection;
222 if (axis_ == Axis::NONE) {
223 panDirection.type = PanDirection::NONE;
224 } else if (axis_ == Axis::VERTICAL) {
225 panDirection.type = PanDirection::VERTICAL;
226 } else {
227 panDirection.type = PanDirection::HORIZONTAL;
228 }
229 if (panRecognizer_) {
230 panRecognizer_->SetDirection(panDirection);
231 }
232 if (panRecognizerNG_) {
233 panRecognizerNG_->SetDirection(panDirection);
234 }
235 }
236
HandleTouchDown()237 void Scrollable::HandleTouchDown()
238 {
239 isTouching_ = true;
240 // If animation still runs, first stop it.
241 springController_->Stop();
242 if (!controller_->IsStopped()) {
243 controller_->Stop();
244 } else if (snapController_->IsRunning()) {
245 snapController_->Stop();
246 } else {
247 // Resets values.
248 currentPos_ = 0.0;
249 }
250 if (!scrollSnapController_->IsStopped()) {
251 scrollSnapController_->Stop();
252 }
253 }
254
HandleTouchUp()255 void Scrollable::HandleTouchUp()
256 {
257 // Two fingers are alternately drag, one finger is released without triggering spring animation.
258 if (isDragging_) {
259 return;
260 }
261 isTouching_ = false;
262 if (outBoundaryCallback_ && !outBoundaryCallback_()) {
263 if (scrollSnapController_->IsStopped() && scrollSnapCallback_) {
264 scrollSnapCallback_(0.0, 0.0);
265 }
266 return;
267 }
268 if (springController_->IsStopped() && scrollOverCallback_) {
269 ProcessScrollOverCallback(0.0);
270 }
271 }
272
HandleTouchCancel()273 void Scrollable::HandleTouchCancel()
274 {
275 isTouching_ = false;
276 if (springController_->IsStopped() && scrollOverCallback_) {
277 ProcessScrollOverCallback(0.0);
278 }
279 }
280
IsAnimationNotRunning() const281 bool Scrollable::IsAnimationNotRunning() const
282 {
283 return !isTouching_ && !controller_->IsRunning() && !springController_->IsRunning() &&
284 !scrollSnapController_->IsRunning();
285 }
286
Idle() const287 bool Scrollable::Idle() const
288 {
289 return !isTouching_ && (controller_->IsStopped() || controller_->GetStatus() == Animator::Status::IDLE) &&
290 (springController_->IsStopped() || springController_->GetStatus() == Animator::Status::IDLE) &&
291 (scrollSnapController_->IsStopped() || scrollSnapController_->GetStatus() == Animator::Status::IDLE) &&
292 (snapController_->IsStopped() || snapController_->GetStatus() == Animator::Status::IDLE);
293 }
294
IsStopped() const295 bool Scrollable::IsStopped() const
296 {
297 return (!springController_ || (springController_->IsStopped()) ||
298 (springController_->GetStatus() == Animator::Status::IDLE)) &&
299 (!controller_ || (controller_->IsStopped()) || (controller_->GetStatus() == Animator::Status::IDLE)) &&
300 (!scrollSnapController_ || (scrollSnapController_->IsStopped()) ||
301 (scrollSnapController_->GetStatus() == Animator::Status::IDLE)) &&
302 (!snapController_ || (snapController_->IsStopped()) ||
303 (snapController_->GetStatus() == Animator::Status::IDLE));
304 }
305
IsSpringStopped() const306 bool Scrollable::IsSpringStopped() const
307 {
308 return !springController_ || (springController_->IsStopped());
309 }
310
IsSnapStopped() const311 bool Scrollable::IsSnapStopped() const
312 {
313 return !snapController_ || (snapController_->IsStopped()) ||
314 (snapController_->GetStatus() == Animator::Status::IDLE);
315 }
316
StopScrollable()317 void Scrollable::StopScrollable()
318 {
319 if (controller_) {
320 controller_->Stop();
321 }
322 if (springController_) {
323 springController_->Stop();
324 }
325 if (scrollSnapController_) {
326 scrollSnapController_->Stop();
327 }
328 if (snapController_) {
329 snapController_->Stop();
330 }
331 }
332
HandleScrollEnd()333 void Scrollable::HandleScrollEnd()
334 {
335 // priority:
336 // 1. onScrollEndRec_ (would internally call onScrollEnd)
337 // 2. scrollEndCallback_
338 if (onScrollEndRec_) {
339 onScrollEndRec_();
340 return;
341 }
342 if (scrollEndCallback_) {
343 scrollEndCallback_();
344 }
345 }
346
HandleDragStart(const OHOS::Ace::GestureEvent & info)347 void Scrollable::HandleDragStart(const OHOS::Ace::GestureEvent& info)
348 {
349 ACE_FUNCTION_TRACE();
350 currentVelocity_ = info.GetMainVelocity();
351 if (dragFRCSceneCallback_) {
352 dragFRCSceneCallback_(currentVelocity_, NG::SceneStatus::START);
353 }
354 if (continuousDragStatus_) {
355 IncreaseContinueDragCount();
356 task_.Cancel();
357 }
358 SetDragStartPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
359 const auto dragPositionInMainAxis =
360 axis_ == Axis::VERTICAL ? info.GetGlobalLocation().GetY() : info.GetGlobalLocation().GetX();
361 #ifdef OHOS_PLATFORM
362 // Increase the cpu frequency when sliding start.
363 auto currentTime = GetSysTimestamp();
364 auto increaseCpuTime = currentTime - startIncreaseTime_;
365 if (!moved_ || increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
366 startIncreaseTime_ = currentTime;
367 ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
368 if (FrameReport::GetInstance().GetEnable()) {
369 FrameReport::GetInstance().BeginListFling();
370 }
371 }
372 #endif
373 JankFrameReport::GetInstance().SetFrameJankFlag(JANK_RUNNING_SCROLL);
374 if (onScrollStartRec_) {
375 onScrollStartRec_(static_cast<float>(dragPositionInMainAxis));
376 }
377 RelatedEventStart();
378 auto node = scrollableNode_.Upgrade();
379 if (node) {
380 node->DispatchCancelPressAnimation();
381 }
382 }
383
HandleScrollParentFirst(double & offset,int32_t source,NestedState state)384 [[deprecated("using NestableScrollContainer")]] ScrollResult Scrollable::HandleScrollParentFirst(
385 double& offset, int32_t source, NestedState state)
386 {
387 auto parent = parent_.Upgrade();
388 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
389 if (state == NestedState::CHILD_OVER_SCROLL) {
390 if (edgeEffect_ == EdgeEffect::NONE) {
391 return parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
392 }
393 ExecuteScrollFrameBegin(offset, scrollState);
394 return { 0, true };
395 }
396 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL);
397 offset = result.remain;
398 if (NearZero(offset)) {
399 canOverScroll_ = false;
400 return { 0, false };
401 }
402 double allOffset = offset;
403 ExecuteScrollFrameBegin(offset, scrollState);
404 auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
405 auto overOffsets = overScrollOffsetCallback_(offset);
406 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
407 remainOffset += overOffset;
408 if (NearZero(remainOffset)) {
409 canOverScroll_ = false;
410 return { 0, false };
411 }
412 if (state == NestedState::CHILD_SCROLL) {
413 offset -= overOffset;
414 canOverScroll_ = false;
415 return { remainOffset, !NearZero(overOffset) };
416 }
417 if (edgeEffect_ == EdgeEffect::NONE) {
418 result = parent->HandleScroll(remainOffset, source, NestedState::CHILD_OVER_SCROLL);
419 }
420 canOverScroll_ = !NearZero(overOffset) || (NearZero(offset) && result.reachEdge);
421 return { 0, canOverScroll_ };
422 }
423
HandleScrollSelfFirst(double & offset,int32_t source,NestedState state)424 [[deprecated("using NestableScrollContainer")]] ScrollResult Scrollable::HandleScrollSelfFirst(
425 double& offset, int32_t source, NestedState state)
426 {
427 auto parent = parent_.Upgrade();
428 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
429 if (state == NestedState::CHILD_OVER_SCROLL) {
430 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
431 if (NearZero(result.remain)) {
432 offset = 0;
433 return result;
434 }
435 ExecuteScrollFrameBegin(offset, scrollState);
436 if (edgeEffect_ == EdgeEffect::NONE) {
437 return result;
438 }
439 return { 0, true };
440 }
441 double allOffset = offset;
442 ExecuteScrollFrameBegin(offset, scrollState);
443 auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
444 auto overOffsets = overScrollOffsetCallback_(offset);
445 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
446 if (NearZero(overOffset) && NearZero(remainOffset)) {
447 canOverScroll_ = false;
448 return { 0, false };
449 }
450 offset -= overOffset;
451 auto result = parent->HandleScroll(overOffset + remainOffset, source, NestedState::CHILD_SCROLL);
452 if (NearZero(result.remain)) {
453 canOverScroll_ = false;
454 return { 0, false };
455 }
456 if (state == NestedState::CHILD_SCROLL) {
457 canOverScroll_ = false;
458 return result;
459 }
460 auto overRes = parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL);
461 offset += std::abs(overOffset) < std::abs(result.remain) ? overOffset : overRes.remain;
462 canOverScroll_ = (!NearZero(overOffset) || NearZero(offset)) && overRes.reachEdge;
463 return { 0, canOverScroll_ };
464 }
465
HandleScrollSelfOnly(double & offset,int32_t source,NestedState state)466 [[deprecated("using NestableScrollContainer")]] ScrollResult Scrollable::HandleScrollSelfOnly(
467 double& offset, int32_t source, NestedState state)
468 {
469 double allOffset = offset;
470 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
471 ExecuteScrollFrameBegin(offset, scrollState);
472 auto remainOffset = allOffset - offset;
473 auto overOffsets = overScrollOffsetCallback_(offset);
474 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
475 remainOffset += overOffset;
476 if (NearZero(remainOffset)) {
477 canOverScroll_ = false;
478 return { 0, false };
479 }
480 bool canOverScroll = false;
481 if (state == NestedState::CHILD_SCROLL) {
482 offset -= overOffset;
483 } else if (state == NestedState::GESTURE) {
484 canOverScroll = !NearZero(overOffset) && edgeEffect_ != EdgeEffect::NONE;
485 } else if (edgeEffect_ != EdgeEffect::NONE) {
486 remainOffset = 0;
487 }
488 canOverScroll_ = canOverScroll;
489 return { remainOffset, !NearZero(overOffset) };
490 }
491
HandleScrollParallel(double & offset,int32_t source,NestedState state)492 [[deprecated("using NestableScrollContainer")]] ScrollResult Scrollable::HandleScrollParallel(
493 double& offset, int32_t source, NestedState state)
494 {
495 auto remainOffset = 0.0;
496 auto parent = parent_.Upgrade();
497 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
498 if (state == NestedState::CHILD_OVER_SCROLL) {
499 if (edgeEffect_ == EdgeEffect::NONE) {
500 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
501 remainOffset = result.remain;
502 offset = 0;
503 } else {
504 ExecuteScrollFrameBegin(offset, scrollState);
505 }
506 return { remainOffset, true };
507 }
508
509 bool canOverScroll = false;
510 double parentOffset = offset;
511 ExecuteScrollFrameBegin(offset, scrollState);
512 auto result = parent->HandleScroll(parentOffset, source, NestedState::CHILD_SCROLL);
513
514 auto overOffsets = overScrollOffsetCallback_(offset);
515 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
516 if (!NearZero(overOffset) && result.reachEdge) {
517 if (state == NestedState::CHILD_SCROLL) {
518 remainOffset = overOffset;
519 offset = offset - overOffset;
520 } else if (edgeEffect_ == EdgeEffect::NONE) {
521 parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL);
522 canOverScroll = true;
523 offset = offset - overOffset;
524 } else {
525 canOverScroll = true;
526 }
527 } else if (!NearZero(overOffset)) {
528 offset = offset - overOffset;
529 }
530 canOverScroll_ = canOverScroll;
531 return { remainOffset, !NearZero(overOffset) && result.reachEdge };
532 }
533
HandleScroll(double offset,int32_t source,NestedState state)534 ScrollResult Scrollable::HandleScroll(double offset, int32_t source, NestedState state)
535 {
536 if (!handleScrollCallback_) {
537 ExecuteScrollBegin(offset);
538 moved_ = UpdateScrollPosition(offset, source);
539 canOverScroll_ = false;
540 return { 0, false };
541 }
542 // call NestableScrollContainer::HandleScroll
543 return handleScrollCallback_(static_cast<float>(offset), source, state);
544 }
545
HandleDragUpdate(const GestureEvent & info)546 void Scrollable::HandleDragUpdate(const GestureEvent& info)
547 {
548 ACE_FUNCTION_TRACE();
549 currentVelocity_ = info.GetMainVelocity();
550 if (dragFRCSceneCallback_) {
551 dragFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
552 }
553 if (!NearZero(info.GetMainVelocity()) && dragCount_ >= FIRST_THRESHOLD) {
554 if (Negative(lastVelocity_ / info.GetMainVelocity())) {
555 ResetContinueDragCount();
556 }
557 }
558 if (!springController_->IsStopped() || !controller_->IsStopped() || !scrollSnapController_->IsStopped() ||
559 !snapController_->IsStopped()) {
560 // If animation still runs, first stop it.
561 isDragUpdateStop_ = true;
562 controller_->Stop();
563 springController_->Stop();
564 scrollSnapController_->Stop();
565 snapController_->Stop();
566 currentPos_ = 0.0;
567 }
568 #ifdef OHOS_PLATFORM
569 // Handle the case where you keep sliding past limit time(4s).
570 auto currentTime = GetSysTimestamp();
571 auto increaseCpuTime = currentTime - startIncreaseTime_;
572 if (increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
573 startIncreaseTime_ = currentTime;
574 ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
575 if (FrameReport::GetInstance().GetEnable()) {
576 FrameReport::GetInstance().BeginListFling();
577 }
578 }
579 #endif
580 auto mainDelta = info.GetMainDelta();
581 if (RelatedScrollEventPrepare(Offset(0.0, mainDelta))) {
582 return;
583 }
584 JankFrameReport::GetInstance().RecordFrameUpdate();
585 auto source = info.GetInputEventType() == InputEventType::AXIS ? SCROLL_FROM_AXIS : SCROLL_FROM_UPDATE;
586 HandleScroll(mainDelta, source, NestedState::GESTURE);
587 }
588
HandleDragEnd(const GestureEvent & info)589 void Scrollable::HandleDragEnd(const GestureEvent& info)
590 {
591 if (dragFRCSceneCallback_) {
592 dragFRCSceneCallback_(info.GetMainVelocity(), NG::SceneStatus::END);
593 }
594 isTouching_ = false;
595 controller_->ClearAllListeners();
596 springController_->ClearAllListeners();
597 scrollSnapController_->ClearAllListeners();
598 isDragUpdateStop_ = false;
599 touchUp_ = false;
600 scrollPause_ = false;
601 lastVelocity_ = info.GetMainVelocity();
602 double correctVelocity =
603 std::clamp(info.GetMainVelocity(), -maxFlingVelocity_ + slipFactor_, maxFlingVelocity_ - slipFactor_);
604 SetDragEndPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
605 correctVelocity = correctVelocity * sVelocityScale_ * GetGain(GetDragOffset());
606 currentVelocity_ = correctVelocity;
607
608 lastPos_ = GetDragOffset();
609 JankFrameReport::GetInstance().ClearFrameJankFlag(JANK_RUNNING_SCROLL);
610 if (dragEndCallback_) {
611 dragEndCallback_();
612 }
613 RelatedEventEnd();
614 double mainPosition = GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY()));
615 if (!moved_ || info.GetInputEventType() == InputEventType::AXIS) {
616 if (calePredictSnapOffsetCallback_) {
617 std::optional<float> predictSnapOffset = calePredictSnapOffsetCallback_(0.0f, 0.0f, 0.0f);
618 if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
619 currentPos_ = mainPosition;
620 ProcessScrollSnapSpringMotion(predictSnapOffset.value(), correctVelocity);
621 return;
622 }
623 }
624 HandleScrollEnd();
625 currentVelocity_ = 0.0;
626 #ifdef OHOS_PLATFORM
627 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
628 if (FrameReport::GetInstance().GetEnable()) {
629 FrameReport::GetInstance().EndListFling();
630 }
631 #endif
632 } else if (!Container::IsCurrentUseNewPipeline() && outBoundaryCallback_ && outBoundaryCallback_() &&
633 scrollOverCallback_) {
634 ResetContinueDragCount();
635 ProcessScrollOverCallback(correctVelocity);
636 } else if (canOverScroll_) {
637 ResetContinueDragCount();
638 HandleOverScroll(correctVelocity);
639 } else {
640 StartScrollAnimation(mainPosition, correctVelocity);
641 }
642 SetDelayedTask();
643 }
644
StartScrollAnimation(float mainPosition,float correctVelocity)645 void Scrollable::StartScrollAnimation(float mainPosition, float correctVelocity)
646 {
647 if (springController_ && !springController_->IsStopped()) {
648 springController_->Stop();
649 }
650 StopSnapController();
651 double friction = friction_ > 0 ? friction_ : sFriction_;
652 if (motion_) {
653 motion_->Reset(friction, mainPosition, correctVelocity);
654 } else {
655 motion_ =
656 AceType::MakeRefPtr<FrictionMotion>(friction, mainPosition, correctVelocity);
657 motion_->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
658 auto scroll = weakScroll.Upgrade();
659 if (scroll) {
660 scroll->ProcessScrollMotion(value);
661 }
662 });
663 }
664 if (calePredictSnapOffsetCallback_) {
665 std::optional<float> predictSnapOffset =
666 calePredictSnapOffsetCallback_(motion_->GetFinalPosition() - mainPosition, GetDragOffset(), correctVelocity);
667 if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
668 currentPos_ = mainPosition;
669 ProcessScrollSnapSpringMotion(predictSnapOffset.value(), correctVelocity);
670 return;
671 }
672 }
673 if (scrollSnapCallback_ && scrollSnapCallback_(motion_->GetFinalPosition() - mainPosition, correctVelocity)) {
674 currentVelocity_ = 0.0;
675 return;
676 }
677 // change motion param when list item need to be center of screen on watch
678 FixScrollMotion(mainPosition);
679 // Resets values.
680 currentPos_ = mainPosition;
681 currentVelocity_ = 0.0;
682 // Starts motion.
683 controller_->ClearStopListeners();
684 controller_->AddStopListener([weak = AceType::WeakClaim(this)]() {
685 auto scroll = weak.Upgrade();
686 if (scroll) {
687 scroll->ProcessScrollMotionStop();
688 // Send event to accessibility when scroll stop.
689 auto context = scroll->GetContext().Upgrade();
690 if (context && scroll->Idle()) {
691 AccessibilityEvent scrollEvent;
692 scrollEvent.nodeId = scroll->nodeId_;
693 scrollEvent.eventType = "scrollend";
694 context->SendEventToAccessibility(scrollEvent);
695 }
696 }
697 });
698 if (scrollMotionFRCSceneCallback_) {
699 scrollMotionFRCSceneCallback_(motion_->GetCurrentVelocity(), NG::SceneStatus::START);
700 }
701 controller_->PlayMotion(motion_);
702 }
703
SetDelayedTask()704 void Scrollable::SetDelayedTask()
705 {
706 SetContinuousDragStatus(true);
707 auto context = PipelineContext::GetCurrentContext();
708 CHECK_NULL_VOID(context);
709 auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
710 task_.Reset([weak = WeakClaim(this)] {
711 auto drag = weak.Upgrade();
712 if (drag) {
713 drag->ResetContinueDragCount();
714 drag->SetContinuousDragStatus(false);
715 }
716 });
717 taskExecutor.PostDelayedTask(task_, DRAG_INTERVAL_TIME, "ArkUIScrollDragInterval");
718 }
719
ComputeCap(int dragCount)720 double Scrollable::ComputeCap(int dragCount)
721 {
722 if (dragCount < FIRST_THRESHOLD) {
723 return 1.0;
724 }
725 auto cap = ComputeCap(dragCount - 1) + CAP_COEFFICIENT * (dragCount - 1);
726 return cap;
727 }
728
GetGain(double delta)729 double Scrollable::GetGain(double delta)
730 {
731 auto cap = 1.0;
732 auto gain = 1.0;
733 if (!continuousSlidingCallback_) {
734 return gain;
735 }
736 auto screenHeight = continuousSlidingCallback_();
737 if (delta == 0 || screenHeight == 0) {
738 return gain;
739 }
740 if (dragCount_ >= FIRST_THRESHOLD && dragCount_ < SECOND_THRESHOLD) {
741 if (Negative(lastPos_ / delta)) {
742 ResetContinueDragCount();
743 return gain;
744 }
745 cap = ComputeCap(dragCount_);
746 gain = (LessNotEqual(cap, std::abs(delta) / screenHeight * (dragCount_ - 1))) ? cap :
747 std::abs(delta) / screenHeight * (dragCount_ - 1);
748 } else if (dragCount_ >= SECOND_THRESHOLD) {
749 if (Negative(lastPos_ / delta)) {
750 ResetContinueDragCount();
751 return gain;
752 }
753 cap = CAP_FIXED_VALUE;
754 gain = (LessNotEqual(cap, std::abs(delta) / screenHeight * (dragCount_ - 1))) ? cap :
755 std::abs(delta) / screenHeight * (dragCount_ - 1);
756 }
757 return gain;
758 }
759
ExecuteScrollBegin(double & mainDelta)760 void Scrollable::ExecuteScrollBegin(double& mainDelta)
761 {
762 auto context = context_.Upgrade();
763 if (!scrollBeginCallback_ || !context) {
764 return;
765 }
766
767 ScrollInfo scrollInfo;
768 if (axis_ == Axis::VERTICAL) {
769 scrollInfo = scrollBeginCallback_(0.0_vp, Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP));
770 mainDelta = context->NormalizeToPx(scrollInfo.dy);
771 } else if (axis_ == Axis::HORIZONTAL) {
772 scrollInfo = scrollBeginCallback_(Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP), 0.0_vp);
773 mainDelta = context->NormalizeToPx(scrollInfo.dx);
774 }
775 }
776
ExecuteScrollFrameBegin(double & mainDelta,ScrollState state)777 [[deprecated]] void Scrollable::ExecuteScrollFrameBegin(double& mainDelta, ScrollState state)
778 {
779 auto context = context_.Upgrade();
780 if (!scrollFrameBeginCallback_ || !context) {
781 return;
782 }
783
784 auto offset = Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP);
785 auto scrollRes = scrollFrameBeginCallback_(-offset, state);
786 mainDelta = -context->NormalizeToPx(scrollRes.offset);
787 }
788
FixScrollMotion(double position)789 void Scrollable::FixScrollMotion(double position)
790 {
791 #ifdef WEARABLE_PRODUCT
792 if (motion_ && needCenterFix_ && watchFixCallback_) {
793 double finalPoisition = watchFixCallback_(motion_->GetFinalPosition(), position);
794 if (!NearEqual(finalPoisition, motion_->GetFinalPosition(), DISTANCE_EPSILON)) {
795 double velocity = motion_->GetVelocityByFinalPosition(finalPoisition);
796 double friction = friction_ > 0 ? friction_ : sFriction_;
797 motion_->Reset(friction, position, velocity);
798
799 // fix again when velocity is less than velocity threshold
800 if (!NearEqual(finalPoisition, motion_->GetFinalPosition(), DISTANCE_EPSILON)) {
801 velocity = motion_->GetVelocityByFinalPosition(finalPoisition, 0.0);
802 motion_->Reset(friction, position, velocity, 0.0);
803 }
804 }
805 }
806 #endif
807 };
808
StartScrollSnapMotion(float predictSnapOffset,float scrollSnapVelocity)809 void Scrollable::StartScrollSnapMotion(float predictSnapOffset, float scrollSnapVelocity)
810 {
811 auto start = currentPos_;
812 auto end = currentPos_ + predictSnapOffset;
813 scrollSnapMotion_ = AceType::MakeRefPtr<SpringMotion>(start, end, scrollSnapVelocity, DEFAULT_OVER_SPRING_PROPERTY);
814
815 scrollSnapMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double position) {
816 auto scroll = weakScroll.Upgrade();
817 if (scroll) {
818 scroll->ProcessScrollSnapMotion(position);
819 }
820 });
821 scrollSnapController_->ClearStopListeners();
822 scrollSnapController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
823 auto scroll = weak.Upgrade();
824 CHECK_NULL_VOID(scroll);
825 scroll->ProcessScrollSnapStop();
826 });
827 if (scrollMotionFRCSceneCallback_) {
828 scrollMotionFRCSceneCallback_(scrollSnapMotion_->GetCurrentVelocity(), NG::SceneStatus::START);
829 }
830 scrollSnapController_->PlayMotion(scrollSnapMotion_);
831 }
832
ProcessScrollSnapSpringMotion(float scrollSnapDelta,float scrollSnapVelocity)833 void Scrollable::ProcessScrollSnapSpringMotion(float scrollSnapDelta, float scrollSnapVelocity)
834 {
835 if (!snapController_) {
836 snapController_ = AceType::MakeRefPtr<Animator>(PipelineBase::GetCurrentContext());
837 snapController_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
838 auto scroll = weakScroll.Upgrade();
839 CHECK_NULL_VOID(scroll);
840 scroll->ProcessScrollMotionStop();
841 // Send event to accessibility when scroll stop.
842 auto context = scroll->GetContext().Upgrade();
843 CHECK_NULL_VOID(context && scroll->Idle());
844 AccessibilityEvent scrollEvent;
845 scrollEvent.nodeId = scroll->nodeId_;
846 scrollEvent.eventType = "scrollend";
847 context->SendEventToAccessibility(scrollEvent);
848 });
849 }
850 if (!snapMotion_) {
851 snapMotion_ = AceType::MakeRefPtr<SpringMotion>(
852 currentPos_, scrollSnapDelta + currentPos_, scrollSnapVelocity, DEFAULT_OVER_SPRING_PROPERTY);
853 snapMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](float position) {
854 auto scroll = weakScroll.Upgrade();
855 if (scroll) {
856 scroll->ProcessScrollMotion(position);
857 }
858 });
859 } else {
860 snapMotion_->Reset(
861 currentPos_, scrollSnapDelta + currentPos_, scrollSnapVelocity, DEFAULT_OVER_SPRING_PROPERTY);
862 }
863 if (scrollMotionFRCSceneCallback_) {
864 scrollMotionFRCSceneCallback_(snapMotion_->GetCurrentVelocity(), NG::SceneStatus::START);
865 }
866 snapController_->PlayMotion(snapMotion_);
867 }
868
UpdateScrollSnapStartOffset(double offset)869 void Scrollable::UpdateScrollSnapStartOffset(double offset)
870 {
871 if (scrollSnapMotion_ && scrollSnapController_ && scrollSnapController_->IsRunning()) {
872 scrollSnapController_->ClearStopListeners();
873 scrollSnapController_->Stop();
874 auto currPos = scrollSnapMotion_->GetCurrentPosition();
875 auto endPos = scrollSnapMotion_->GetEndValue();
876 auto velocity = scrollSnapMotion_->GetCurrentVelocity();
877 scrollSnapMotion_->Reset(currPos + offset, endPos, velocity, DEFAULT_OVER_SPRING_PROPERTY);
878 scrollSnapController_->PlayMotion(scrollSnapMotion_);
879 scrollSnapController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
880 auto scroll = weak.Upgrade();
881 CHECK_NULL_VOID(scroll);
882 scroll->ProcessScrollSnapStop();
883 });
884 }
885 }
886
ProcessScrollSnapMotion(double position)887 void Scrollable::ProcessScrollSnapMotion(double position)
888 {
889 currentVelocity_ = scrollSnapMotion_->GetCurrentVelocity();
890 if (scrollMotionFRCSceneCallback_) {
891 scrollMotionFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
892 }
893 if (NearEqual(currentPos_, position)) {
894 UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION_SPRING);
895 } else {
896 auto mainDelta = position - currentPos_;
897 HandleScroll(mainDelta, SCROLL_FROM_ANIMATION, NestedState::GESTURE);
898 if (!moved_) {
899 scrollSnapController_->Stop();
900 } else if (!touchUp_) {
901 if (scrollTouchUpCallback_) {
902 scrollTouchUpCallback_();
903 }
904 touchUp_ = true;
905 }
906 }
907 currentPos_ = scrollSnapMotion_->GetCurrentPosition();
908 if (outBoundaryCallback_ && outBoundaryCallback_()) {
909 scrollPause_ = true;
910 scrollSnapController_->Stop();
911 }
912 }
913
ProcessScrollSnapStop()914 void Scrollable::ProcessScrollSnapStop()
915 {
916 if (scrollSnapMotion_ && scrollMotionFRCSceneCallback_) {
917 scrollMotionFRCSceneCallback_(scrollSnapMotion_->GetCurrentVelocity(), NG::SceneStatus::END);
918 }
919 if (scrollPause_) {
920 scrollPause_ = false;
921 HandleOverScroll(currentVelocity_);
922 } else {
923 OnAnimateStop();
924 }
925 }
926
OnAnimateStop()927 void Scrollable::OnAnimateStop()
928 {
929 if (scrollMotion_ && scrollMotion_->IsValid() && scrollMotionFRCSceneCallback_) {
930 scrollMotionFRCSceneCallback_(scrollMotion_->GetCurrentVelocity(), NG::SceneStatus::END);
931 }
932 if (moved_) {
933 HandleScrollEnd();
934 }
935 currentVelocity_ = 0.0;
936 if (isTouching_ || isDragUpdateStop_) {
937 return;
938 }
939 moved_ = false;
940 #ifdef OHOS_PLATFORM
941 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
942 if (FrameReport::GetInstance().GetEnable()) {
943 FrameReport::GetInstance().EndListFling();
944 }
945 #endif
946 if (scrollEnd_) {
947 scrollEnd_();
948 }
949 // Send event to accessibility when scroll stop.
950 auto context = GetContext().Upgrade();
951 if (context) {
952 AccessibilityEvent scrollEvent;
953 scrollEvent.nodeId = nodeId_;
954 scrollEvent.eventType = "scrollend";
955 context->SendEventToAccessibility(scrollEvent);
956 }
957 #if !defined(PREVIEW)
958 LayoutInspector::SupportInspector();
959 #endif
960 }
961
StartSpringMotion(double mainPosition,double mainVelocity,const ExtentPair & extent,const ExtentPair & initExtent)962 void Scrollable::StartSpringMotion(
963 double mainPosition, double mainVelocity, const ExtentPair& extent, const ExtentPair& initExtent)
964 {
965 scrollMotion_ = AceType::MakeRefPtr<ScrollMotion>(mainPosition, mainVelocity, extent, initExtent, spring_);
966 if (!scrollMotion_->IsValid()) {
967 return;
968 }
969 scrollMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double position) {
970 auto scroll = weakScroll.Upgrade();
971 if (scroll) {
972 scroll->ProcessSpringMotion(position);
973 }
974 });
975 currentPos_ = mainPosition;
976 springController_->ClearStopListeners();
977 if (scrollMotionFRCSceneCallback_) {
978 scrollMotionFRCSceneCallback_(scrollMotion_->GetCurrentVelocity(), NG::SceneStatus::START);
979 }
980 springController_->PlayMotion(scrollMotion_);
981 springController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
982 auto scroll = weak.Upgrade();
983 CHECK_NULL_VOID(scroll);
984 scroll->OnAnimateStop();
985 });
986 }
987
ProcessScrollMotionStop()988 void Scrollable::ProcessScrollMotionStop()
989 {
990 if (motion_ && scrollMotionFRCSceneCallback_) {
991 scrollMotionFRCSceneCallback_(motion_->GetCurrentVelocity(), NG::SceneStatus::END);
992 }
993 if (snapMotion_ && scrollMotionFRCSceneCallback_) {
994 scrollMotionFRCSceneCallback_(snapMotion_->GetCurrentVelocity(), NG::SceneStatus::END);
995 }
996 if (needScrollSnapChange_ && calePredictSnapOffsetCallback_ && motion_) {
997 needScrollSnapChange_ = false;
998 auto predictSnapOffset = calePredictSnapOffsetCallback_(motion_->GetFinalPosition() - currentPos_, 0.0f, 0.0f);
999 if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
1000 ProcessScrollSnapSpringMotion(predictSnapOffset.value(), currentVelocity_);
1001 return;
1002 }
1003 }
1004 // spring effect special process
1005 if (scrollPause_) {
1006 scrollPause_ = false;
1007 HandleOverScroll(currentVelocity_);
1008 } else {
1009 if (isDragUpdateStop_) {
1010 return;
1011 }
1012 moved_ = false;
1013 HandleScrollEnd();
1014 #ifdef OHOS_PLATFORM
1015 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
1016 if (FrameReport::GetInstance().GetEnable()) {
1017 FrameReport::GetInstance().EndListFling();
1018 }
1019 #endif
1020 if (scrollEnd_) {
1021 scrollEnd_();
1022 }
1023 currentVelocity_ = 0.0;
1024 #if !defined(PREVIEW)
1025 LayoutInspector::SupportInspector();
1026 #endif
1027 }
1028 }
1029
ProcessSpringMotion(double position)1030 void Scrollable::ProcessSpringMotion(double position)
1031 {
1032 currentVelocity_ = scrollMotion_->GetCurrentVelocity();
1033 if (scrollMotionFRCSceneCallback_) {
1034 scrollMotionFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
1035 }
1036 if (NearEqual(currentPos_, position)) {
1037 UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION_SPRING);
1038 } else {
1039 moved_ = UpdateScrollPosition(position - currentPos_, SCROLL_FROM_ANIMATION_SPRING);
1040 if (!moved_) {
1041 springController_->Stop();
1042 } else if (!touchUp_) {
1043 if (scrollTouchUpCallback_) {
1044 scrollTouchUpCallback_();
1045 }
1046 touchUp_ = true;
1047 }
1048 }
1049 currentPos_ = position;
1050 }
1051
ProcessScrollMotion(double position)1052 void Scrollable::ProcessScrollMotion(double position)
1053 {
1054 if (motion_) {
1055 currentVelocity_ = motion_->GetCurrentVelocity();
1056 }
1057 if (scrollMotionFRCSceneCallback_) {
1058 scrollMotionFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
1059 }
1060 if (snapMotion_ && scrollMotionFRCSceneCallback_) {
1061 scrollMotionFRCSceneCallback_(snapMotion_->GetCurrentVelocity(), NG::SceneStatus::RUNNING);
1062 }
1063 if (needScrollSnapToSideCallback_) {
1064 needScrollSnapChange_ = needScrollSnapToSideCallback_(position - currentPos_);
1065 }
1066 if ((NearEqual(currentPos_, position))) {
1067 UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION);
1068 } else {
1069 // UpdateScrollPosition return false, means reach to scroll limit.
1070 auto mainDelta = position - currentPos_;
1071 HandleScroll(mainDelta, SCROLL_FROM_ANIMATION, NestedState::GESTURE);
1072 if (!moved_) {
1073 controller_->Stop();
1074 } else if (!touchUp_) {
1075 if (scrollTouchUpCallback_) {
1076 scrollTouchUpCallback_();
1077 }
1078 touchUp_ = true;
1079 }
1080 }
1081 currentPos_ = position;
1082
1083 // spring effect special process
1084 if ((IsSnapStopped() && canOverScroll_) || needScrollSnapChange_ ||
1085 (!Container::IsCurrentUseNewPipeline() && outBoundaryCallback_ && outBoundaryCallback_())) {
1086 scrollPause_ = true;
1087 controller_->Stop();
1088 }
1089 }
1090
UpdateScrollPosition(const double offset,int32_t source) const1091 bool Scrollable::UpdateScrollPosition(const double offset, int32_t source) const
1092 {
1093 bool ret = true;
1094 if (callback_) {
1095 ret = callback_(offset, source);
1096 }
1097 return ret;
1098 }
1099
ProcessScrollOverCallback(double velocity)1100 void Scrollable::ProcessScrollOverCallback(double velocity)
1101 {
1102 if (outBoundaryCallback_ && !outBoundaryCallback_() && !canOverScroll_) {
1103 return;
1104 }
1105 // In the case of chain animation enabled, you need to switch the control point first,
1106 // and then correct the offset value in notification process
1107 if (notifyScrollOverCallback_) {
1108 notifyScrollOverCallback_(velocity);
1109 }
1110 // then use corrected offset to make scroll motion.
1111 if (scrollOverCallback_) {
1112 scrollOverCallback_(velocity);
1113 }
1114 }
1115
HandleOverScroll(double velocity)1116 bool Scrollable::HandleOverScroll(double velocity)
1117 {
1118 if (!overScrollCallback_) {
1119 if (edgeEffect_ == EdgeEffect::SPRING) {
1120 ProcessScrollOverCallback(velocity);
1121 return true;
1122 }
1123 if (scrollEndCallback_) {
1124 scrollEndCallback_();
1125 }
1126 return false;
1127 }
1128 // call NestableScrollContainer::HandleOverScroll
1129 return overScrollCallback_(velocity);
1130 }
1131
SetSlipFactor(double SlipFactor)1132 void Scrollable::SetSlipFactor(double SlipFactor)
1133 {
1134 slipFactor_ = std::clamp(SlipFactor, -ADJUSTABLE_VELOCITY, ADJUSTABLE_VELOCITY);
1135 }
1136
GetDefaultOverSpringProperty()1137 const RefPtr<SpringProperty>& Scrollable::GetDefaultOverSpringProperty()
1138 {
1139 return DEFAULT_OVER_SPRING_PROPERTY;
1140 }
1141
UpdateScrollSnapEndWithOffset(double offset)1142 void Scrollable::UpdateScrollSnapEndWithOffset(double offset)
1143 {
1144 if (scrollSnapMotion_ && scrollSnapController_ && scrollSnapController_->IsRunning()) {
1145 scrollSnapController_->ClearStopListeners();
1146 scrollSnapController_->Stop();
1147 auto currPos = scrollSnapMotion_->GetCurrentPosition();
1148 auto endPos = scrollSnapMotion_->GetEndValue();
1149 auto velocity = scrollSnapMotion_->GetCurrentVelocity();
1150 scrollSnapMotion_->Reset(currPos, endPos - offset, velocity, DEFAULT_OVER_SPRING_PROPERTY);
1151 scrollSnapController_->PlayMotion(scrollSnapMotion_);
1152 scrollSnapController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
1153 auto scroll = weak.Upgrade();
1154 CHECK_NULL_VOID(scroll);
1155 scroll->ProcessScrollSnapStop();
1156 });
1157 }
1158 }
1159 } // namespace OHOS::Ace
1160