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