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