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