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/log.h"
23 #include "base/ressched/ressched_report.h"
24 #include "base/utils/time_util.h"
25 #include "core/common/container.h"
26 #include "core/event/ace_events.h"
27 #include "core/common/layout_inspector.h"
28
29 namespace OHOS::Ace {
30 namespace {
31
32 constexpr double SPRING_SCROLL_MASS = 0.5;
33 constexpr double SPRING_SCROLL_STIFFNESS = 100.0;
34 constexpr double SPRING_SCROLL_DAMPING = 15.55635;
35 const RefPtr<SpringProperty> DEFAULT_OVER_SPRING_PROPERTY =
36 AceType::MakeRefPtr<SpringProperty>(SPRING_SCROLL_MASS, SPRING_SCROLL_STIFFNESS, SPRING_SCROLL_DAMPING);
37 #ifndef WEARABLE_PRODUCT
38 constexpr double FRICTION = 0.6;
39 constexpr double VELOCITY_SCALE = 1.0;
40 constexpr double MAX_VELOCITY = 800000.0;
41 constexpr double MIN_VELOCITY = -800000.0;
42 constexpr double ADJUSTABLE_VELOCITY = 3000.0;
43 #else
44 constexpr double DISTANCE_EPSILON = 1.0;
45 constexpr double FRICTION = 0.9;
46 constexpr double VELOCITY_SCALE = 0.8;
47 constexpr double MAX_VELOCITY = 5000.0;
48 constexpr double MIN_VELOCITY = -5000.0;
49 constexpr double ADJUSTABLE_VELOCITY = 0.0;
50 #endif
51
52 #ifdef OHOS_PLATFORM
53 constexpr int64_t INCREASE_CPU_TIME_ONCE = 4000000000; // 4s(unit: ns)
54 #endif
55
56 } // namespace
57
58 // Static Functions.
59 double Scrollable::sFriction_ = FRICTION;
60 double Scrollable::sVelocityScale_ = VELOCITY_SCALE;
61
SetVelocityScale(double sVelocityScale)62 void Scrollable::SetVelocityScale(double sVelocityScale)
63 {
64 if (LessOrEqual(sVelocityScale, 0.0)) {
65 LOGW("Invalid velocity scale: %{public}lf", sVelocityScale);
66 return;
67 }
68 sVelocityScale_ = sVelocityScale;
69 }
70
SetFriction(double sFriction)71 void Scrollable::SetFriction(double sFriction)
72 {
73 if (LessOrEqual(sFriction, 0.0)) {
74 LOGW("Invalid friction value: %{public}lf", sFriction);
75 return;
76 }
77 sFriction_ = sFriction;
78 }
79
~Scrollable()80 Scrollable::~Scrollable()
81 {
82 // If animation still runs, force stop it.
83 controller_->Stop();
84 springController_->Stop();
85 }
86
OnFlushTouchEventsBegin()87 void Scrollable::OnFlushTouchEventsBegin()
88 {
89 if (panRecognizer_) {
90 panRecognizer_->OnFlushTouchEventsBegin();
91 }
92 }
93
OnFlushTouchEventsEnd()94 void Scrollable::OnFlushTouchEventsEnd()
95 {
96 if (panRecognizer_) {
97 panRecognizer_->OnFlushTouchEventsEnd();
98 }
99 }
100
Initialize(const WeakPtr<PipelineBase> & context)101 void Scrollable::Initialize(const WeakPtr<PipelineBase>& context)
102 {
103 context_ = context;
104 PanDirection panDirection;
105 if (axis_ == Axis::VERTICAL) {
106 panDirection.type = PanDirection::VERTICAL;
107 } else {
108 panDirection.type = PanDirection::HORIZONTAL;
109 }
110
111 auto actionStart = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
112 auto scroll = weakScroll.Upgrade();
113 if (scroll) {
114 // Send event to accessibility when scroll start.
115 auto context = scroll->GetContext().Upgrade();
116 if (context) {
117 AccessibilityEvent scrollEvent;
118 scrollEvent.nodeId = scroll->nodeId_;
119 scrollEvent.eventType = "scrollstart";
120 context->SendEventToAccessibility(scrollEvent);
121 }
122 scroll->HandleDragStart(info);
123 }
124 };
125
126 auto actionUpdate = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
127 auto scroll = weakScroll.Upgrade();
128 if (scroll) {
129 scroll->HandleDragUpdate(info);
130 }
131 };
132
133 auto actionEnd = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
134 auto scroll = weakScroll.Upgrade();
135 if (scroll) {
136 scroll->HandleDragEnd(info);
137 // Send event to accessibility when scroll stop.
138 auto context = scroll->GetContext().Upgrade();
139 if (context && scroll->IsStopped()) {
140 AccessibilityEvent scrollEvent;
141 scrollEvent.nodeId = scroll->nodeId_;
142 scrollEvent.eventType = "scrollend";
143 context->SendEventToAccessibility(scrollEvent);
144 }
145 }
146 };
147
148 auto actionCancel = [weakScroll = AceType::WeakClaim(this)]() {
149 auto scroll = weakScroll.Upgrade();
150 if (!scroll) {
151 return;
152 }
153 if (scroll->dragCancelCallback_) {
154 scroll->dragCancelCallback_();
155 }
156 };
157
158 if (Container::IsCurrentUseNewPipeline()) {
159 panRecognizerNG_ =
160 AceType::MakeRefPtr<NG::PanRecognizer>(DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE);
161
162 panRecognizerNG_->SetOnActionStart(actionStart);
163 panRecognizerNG_->SetOnActionUpdate(actionUpdate);
164 panRecognizerNG_->SetOnActionEnd(actionEnd);
165 panRecognizerNG_->SetOnActionCancel(actionCancel);
166 } else {
167 panRecognizer_ =
168 AceType::MakeRefPtr<PanRecognizer>(context, DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE);
169 panRecognizer_->SetOnActionStart(actionStart);
170 panRecognizer_->SetOnActionUpdate(actionUpdate);
171 panRecognizer_->SetOnActionEnd(actionEnd);
172 panRecognizer_->SetOnActionCancel(actionCancel);
173 }
174
175 // use RawRecognizer to receive next touch down event to stop animation.
176 rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
177
178 rawRecognizer_->SetOnTouchDown([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
179 auto scroll = weakScroll.Upgrade();
180 if (scroll) {
181 scroll->HandleTouchDown();
182 }
183 });
184 rawRecognizer_->SetOnTouchUp([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
185 auto scroll = weakScroll.Upgrade();
186 if (scroll) {
187 scroll->HandleTouchUp();
188 }
189 });
190 rawRecognizer_->SetOnTouchCancel([weakScroll = AceType::WeakClaim(this)](const TouchEventInfo&) {
191 auto scroll = weakScroll.Upgrade();
192 if (scroll) {
193 scroll->HandleTouchCancel();
194 }
195 });
196
197 controller_ = AceType::MakeRefPtr<Animator>(context);
198 springController_ = AceType::MakeRefPtr<Animator>(context);
199
200 spring_ = GetDefaultOverSpringProperty();
201 available_ = true;
202 }
203
SetAxis(Axis axis)204 void Scrollable::SetAxis(Axis axis)
205 {
206 axis_ = axis;
207 PanDirection panDirection;
208 if (axis_ == Axis::VERTICAL) {
209 panDirection.type = PanDirection::VERTICAL;
210 } else {
211 panDirection.type = PanDirection::HORIZONTAL;
212 }
213 if (panRecognizer_) {
214 panRecognizer_->SetDirection(panDirection);
215 }
216 if (panRecognizerNG_) {
217 panRecognizerNG_->SetDirection(panDirection);
218 }
219 }
220
HandleTouchDown()221 void Scrollable::HandleTouchDown()
222 {
223 LOGD("handle touch down");
224 isTouching_ = true;
225 // If animation still runs, first stop it.
226 springController_->Stop();
227 if (!controller_->IsStopped()) {
228 controller_->Stop();
229 } else {
230 // Resets values.
231 currentPos_ = 0.0;
232 }
233 LOGD("handle touch down has already stopped the animation");
234 }
235
HandleTouchUp()236 void Scrollable::HandleTouchUp()
237 {
238 LOGD("handle touch up");
239 isTouching_ = false;
240 if (outBoundaryCallback_ && !outBoundaryCallback_()) {
241 return;
242 }
243 if (springController_->IsStopped() && scrollOverCallback_) {
244 LOGD("need scroll to boundary");
245 ProcessScrollOverCallback(0.0);
246 }
247 }
248
HandleTouchCancel()249 void Scrollable::HandleTouchCancel()
250 {
251 LOGD("handle touch cancel");
252 isTouching_ = false;
253 if (springController_->IsStopped() && scrollOverCallback_) {
254 ProcessScrollOverCallback(0.0);
255 }
256 }
257
IsAnimationNotRunning() const258 bool Scrollable::IsAnimationNotRunning() const
259 {
260 return !isTouching_ && !controller_->IsRunning() && !springController_->IsRunning();
261 }
262
Idle() const263 bool Scrollable::Idle() const
264 {
265 return !isTouching_ && controller_->IsStopped() && springController_->IsStopped();
266 }
267
IsStopped() const268 bool Scrollable::IsStopped() const
269 {
270 return (!springController_ || (springController_->IsStopped())) && (!controller_ || (controller_->IsStopped()));
271 }
272
IsSpringStopped() const273 bool Scrollable::IsSpringStopped() const
274 {
275 return !springController_ || (springController_->IsStopped());
276 }
277
StopScrollable()278 void Scrollable::StopScrollable()
279 {
280 if (controller_) {
281 controller_->Stop();
282 }
283 if (springController_) {
284 springController_->Stop();
285 }
286 }
287
HandleDragStart(const OHOS::Ace::GestureEvent & info)288 void Scrollable::HandleDragStart(const OHOS::Ace::GestureEvent& info)
289 {
290 ACE_FUNCTION_TRACE();
291 const auto dragPositionInMainAxis =
292 axis_ == Axis::VERTICAL ? info.GetGlobalLocation().GetY() : info.GetGlobalLocation().GetX();
293 LOGD("HandleDragStart. LocalLocation: %{public}s, GlobalLocation: %{public}s",
294 info.GetLocalLocation().ToString().c_str(), info.GetGlobalLocation().ToString().c_str());
295 #ifdef OHOS_PLATFORM
296 // Increase the cpu frequency when sliding start.
297 auto currentTime = GetSysTimestamp();
298 auto increaseCpuTime = currentTime - startIncreaseTime_;
299 if (!moved_ || increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
300 LOGI("HandleDragStart increase cpu frequency, moved_ = %{public}d", moved_);
301 startIncreaseTime_ = currentTime;
302 ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
303 if (FrameReport::GetInstance().GetEnable()) {
304 FrameReport::GetInstance().BeginListFling();
305 }
306 }
307 #endif
308 UpdateScrollPosition(dragPositionInMainAxis, SCROLL_FROM_START);
309 RelatedEventStart();
310 auto node = scrollableNode_.Upgrade();
311 if (node) {
312 node->DispatchCancelPressAnimation();
313 }
314 }
315
HandleDragUpdate(const GestureEvent & info)316 void Scrollable::HandleDragUpdate(const GestureEvent& info)
317 {
318 ACE_FUNCTION_TRACE();
319 if (!springController_->IsStopped() || !controller_->IsStopped()) {
320 // If animation still runs, first stop it.
321 isDragUpdateStop_ = true;
322 controller_->Stop();
323 springController_->Stop();
324 currentPos_ = 0.0;
325 }
326 #ifdef OHOS_PLATFORM
327 // Handle the case where you keep sliding past limit time(4s).
328 auto currentTime = GetSysTimestamp();
329 auto increaseCpuTime = currentTime - startIncreaseTime_;
330 if (increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
331 LOGI("HandleDragUpdate increase cpu frequency, moved_ = %{public}d", moved_);
332 startIncreaseTime_ = currentTime;
333 ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
334 if (FrameReport::GetInstance().GetEnable()) {
335 FrameReport::GetInstance().BeginListFling();
336 }
337 }
338 #endif
339 LOGD("handle drag update, offset is %{public}lf", info.GetMainDelta());
340 auto mainDelta = info.GetMainDelta();
341 if (RelatedScrollEventPrepare(Offset(0.0, mainDelta))) {
342 return;
343 }
344 ExecuteScrollBegin(mainDelta);
345 ExecuteScrollFrameBegin(mainDelta, ScrollState::SCROLL);
346 auto source = info.GetInputEventType() == InputEventType::AXIS ? SCROLL_FROM_AXIS : SCROLL_FROM_UPDATE;
347 moved_ = UpdateScrollPosition(mainDelta, source);
348 }
349
HandleDragEnd(const GestureEvent & info)350 void Scrollable::HandleDragEnd(const GestureEvent& info)
351 {
352 LOGD("handle drag end, position is %{public}lf and %{public}lf, velocity is %{public}lf",
353 info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY(), info.GetMainVelocity());
354 controller_->ClearAllListeners();
355 springController_->ClearAllListeners();
356 isDragUpdateStop_ = false;
357 touchUp_ = false;
358 scrollPause_ = false;
359 double correctVelocity = std::clamp(info.GetMainVelocity(), MIN_VELOCITY + slipFactor_, MAX_VELOCITY - slipFactor_);
360 correctVelocity = correctVelocity * sVelocityScale_;
361 currentVelocity_ = correctVelocity;
362 if (dragEndCallback_) {
363 dragEndCallback_();
364 }
365 RelatedEventEnd();
366 if (!moved_ || info.GetInputEventType() == InputEventType::AXIS) {
367 LOGI("It is not moved now, no need to handle drag end motion");
368 if (scrollEndCallback_) {
369 scrollEndCallback_();
370 }
371 currentVelocity_ = 0.0;
372 return;
373 }
374 if (outBoundaryCallback_ && outBoundaryCallback_() && scrollOverCallback_) {
375 ProcessScrollOverCallback(correctVelocity);
376 } else {
377 double mainPosition = GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY()));
378 LOGD("[scrollMotion]position(%{public}lf), velocity(%{public}lf)", mainPosition, correctVelocity);
379 if (motion_) {
380 motion_->Reset(sFriction_, mainPosition, correctVelocity);
381 } else {
382 motion_ = AceType::MakeRefPtr<FrictionMotion>(sFriction_, mainPosition, correctVelocity);
383 motion_->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
384 auto scroll = weakScroll.Upgrade();
385 if (scroll) {
386 scroll->ProcessScrollMotion(value);
387 }
388 });
389 }
390
391 // change motion param when list item need to be center of screen on watch
392 FixScrollMotion(mainPosition);
393
394 // Resets values.
395 currentPos_ = mainPosition;
396 currentVelocity_ = 0.0;
397
398 // Starts motion.
399 controller_->ClearStopListeners();
400 controller_->AddStopListener([weak = AceType::WeakClaim(this)]() {
401 auto scroll = weak.Upgrade();
402 if (scroll) {
403 scroll->ProcessScrollMotionStop();
404 // Send event to accessibility when scroll stop.
405 auto context = scroll->GetContext().Upgrade();
406 if (context && scroll->Idle()) {
407 AccessibilityEvent scrollEvent;
408 scrollEvent.nodeId = scroll->nodeId_;
409 scrollEvent.eventType = "scrollend";
410 context->SendEventToAccessibility(scrollEvent);
411 }
412 }
413 });
414 controller_->PlayMotion(motion_);
415 }
416 }
417
ExecuteScrollBegin(double & mainDelta)418 void Scrollable::ExecuteScrollBegin(double& mainDelta)
419 {
420 auto context = context_.Upgrade();
421 if (!scrollBeginCallback_ || !context) {
422 return;
423 }
424
425 ScrollInfo scrollInfo;
426 if (axis_ == Axis::VERTICAL) {
427 scrollInfo = scrollBeginCallback_(0.0_vp, Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP));
428 mainDelta = context->NormalizeToPx(scrollInfo.dy);
429 } else if (axis_ == Axis::HORIZONTAL) {
430 scrollInfo = scrollBeginCallback_(Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP), 0.0_vp);
431 mainDelta = context->NormalizeToPx(scrollInfo.dx);
432 }
433 }
434
ExecuteScrollFrameBegin(double & mainDelta,ScrollState state)435 void Scrollable::ExecuteScrollFrameBegin(double& mainDelta, ScrollState state)
436 {
437 auto context = context_.Upgrade();
438 if (!scrollFrameBeginCallback_ || !context) {
439 return;
440 }
441
442 auto offset = Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP);
443 auto scrollRes = scrollFrameBeginCallback_(-offset, state);
444 mainDelta = -context->NormalizeToPx(scrollRes.offset);
445 }
446
FixScrollMotion(double position)447 void Scrollable::FixScrollMotion(double position)
448 {
449 #ifdef WEARABLE_PRODUCT
450 if (motion_ && needCenterFix_ && watchFixCallback_) {
451 double finalPoisition = watchFixCallback_(motion_->GetFinalPosition(), position);
452 LOGD("final position before fix(%{public}lf), need to fix to position(%{public}lf)",
453 motion_->GetFinalPosition(), finalPoisition);
454 if (!NearEqual(finalPoisition, motion_->GetFinalPosition(), DISTANCE_EPSILON)) {
455 double velocity = motion_->GetVelocityByFinalPosition(finalPoisition);
456 motion_->Reset(sFriction_, position, velocity);
457
458 // fix again when velocity is less than velocity threshold
459 if (!NearEqual(finalPoisition, motion_->GetFinalPosition(), DISTANCE_EPSILON)) {
460 velocity = motion_->GetVelocityByFinalPosition(finalPoisition, 0.0);
461 motion_->Reset(sFriction_, position, velocity, 0.0);
462 }
463 LOGD("final position after fix (%{public}lf), ", motion_->GetFinalPosition());
464 }
465 }
466 #endif
467 };
468
StartSpringMotion(double mainPosition,double mainVelocity,const ExtentPair & extent,const ExtentPair & initExtent)469 void Scrollable::StartSpringMotion(
470 double mainPosition, double mainVelocity, const ExtentPair& extent, const ExtentPair& initExtent)
471 {
472 LOGD("[scroll] position(%{public}lf), mainVelocity(%{public}lf), minExtent(%{public}lf), maxExtent(%{public}lf), "
473 "initMinExtent(%{public}lf), initMaxExtent(%{public}lf",
474 mainPosition, mainVelocity, extent.Leading(), extent.Trailing(), initExtent.Leading(), initExtent.Trailing());
475 scrollMotion_ = AceType::MakeRefPtr<ScrollMotion>(mainPosition, mainVelocity, extent, initExtent, spring_);
476 if (!scrollMotion_->IsValid()) {
477 LOGE("scrollMotion is invalid, no available spring motion.");
478 return;
479 }
480 scrollMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double position) {
481 auto scroll = weakScroll.Upgrade();
482 if (scroll) {
483 scroll->ProcessSpringMotion(position);
484 }
485 });
486 currentPos_ = mainPosition;
487 springController_->ClearStopListeners();
488 springController_->PlayMotion(scrollMotion_);
489 springController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
490 auto scroll = weak.Upgrade();
491 if (scroll) {
492 if (scroll->moved_ && scroll->scrollEndCallback_) {
493 scroll->scrollEndCallback_();
494 }
495 scroll->currentVelocity_ = 0.0;
496 if (scroll->isTouching_ || scroll->isDragUpdateStop_) {
497 return;
498 }
499 scroll->moved_ = false;
500 #ifdef OHOS_PLATFORM
501 LOGI("springController stop increase cpu frequency");
502 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
503 if (FrameReport::GetInstance().GetEnable()) {
504 FrameReport::GetInstance().EndListFling();
505 }
506 #endif
507 if (scroll->scrollEnd_) {
508 scroll->scrollEnd_();
509 }
510 // Send event to accessibility when scroll stop.
511 auto context = scroll->GetContext().Upgrade();
512 if (context) {
513 AccessibilityEvent scrollEvent;
514 scrollEvent.nodeId = scroll->nodeId_;
515 scrollEvent.eventType = "scrollend";
516 context->SendEventToAccessibility(scrollEvent);
517 }
518 #if !defined(PREVIEW)
519 LayoutInspector::SupportInspector();
520 #endif
521 }
522 });
523 }
524
ProcessScrollMotionStop()525 void Scrollable::ProcessScrollMotionStop()
526 {
527 if (!scrollPause_ && moved_ && scrollEndCallback_) {
528 scrollEndCallback_();
529 }
530
531 // spring effect special process
532 if (scrollPause_ && scrollOverCallback_) {
533 scrollPause_ = false;
534 ProcessScrollOverCallback(currentVelocity_);
535 } else {
536 currentVelocity_ = 0.0;
537 if (isDragUpdateStop_) {
538 return;
539 }
540 moved_ = false;
541 #ifdef OHOS_PLATFORM
542 LOGI("controller stop increase cpu frequency");
543 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
544 if (FrameReport::GetInstance().GetEnable()) {
545 FrameReport::GetInstance().EndListFling();
546 }
547 #endif
548 if (scrollEnd_) {
549 scrollEnd_();
550 }
551 #if !defined(PREVIEW)
552 LayoutInspector::SupportInspector();
553 #endif
554 }
555 }
556
ProcessSpringMotion(double position)557 void Scrollable::ProcessSpringMotion(double position)
558 {
559 LOGD("[scroll] currentPos_(%{public}lf), position(%{public}lf)", currentPos_, position);
560 currentVelocity_ = scrollMotion_->GetCurrentVelocity();
561 if (NearEqual(currentPos_, position)) {
562 UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION_SPRING);
563 } else {
564 moved_ = UpdateScrollPosition(position - currentPos_, SCROLL_FROM_ANIMATION_SPRING);
565 if (!moved_) {
566 springController_->Stop();
567 } else if (!touchUp_) {
568 if (scrollTouchUpCallback_) {
569 scrollTouchUpCallback_();
570 }
571 touchUp_ = true;
572 }
573 }
574 currentPos_ = position;
575 }
576
ProcessScrollMotion(double position)577 void Scrollable::ProcessScrollMotion(double position)
578 {
579 currentVelocity_ = motion_->GetCurrentVelocity();
580 LOGD("[scrolling] position(%{public}lf), currentVelocity_(%{public}lf)", position, currentVelocity_);
581 if ((NearEqual(currentPos_, position))) {
582 UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION);
583 } else {
584 // UpdateScrollPosition return false, means reach to scroll limit.
585 auto mainDelta = position - currentPos_;
586 ExecuteScrollBegin(mainDelta);
587 ExecuteScrollFrameBegin(mainDelta, ScrollState::FLING);
588 if (!UpdateScrollPosition(mainDelta, SCROLL_FROM_ANIMATION)) {
589 controller_->Stop();
590 } else if (!touchUp_) {
591 if (scrollTouchUpCallback_) {
592 scrollTouchUpCallback_();
593 }
594 touchUp_ = true;
595 }
596 }
597 currentPos_ = position;
598
599 // spring effect special process
600 if (outBoundaryCallback_ && outBoundaryCallback_()) {
601 scrollPause_ = true;
602 controller_->Stop();
603 }
604 }
605
UpdateScrollPosition(const double offset,int32_t source) const606 bool Scrollable::UpdateScrollPosition(const double offset, int32_t source) const
607 {
608 bool ret = true;
609 if (callback_) {
610 ret = callback_(offset, source);
611 }
612 return ret;
613 }
614
ProcessScrollOverCallback(double velocity)615 void Scrollable::ProcessScrollOverCallback(double velocity)
616 {
617 if (outBoundaryCallback_ && !outBoundaryCallback_()) {
618 return;
619 }
620 // In the case of chain animation enabled, you need to switch the control point first,
621 // and then correct the offset value in notification process
622 if (notifyScrollOverCallback_) {
623 notifyScrollOverCallback_(velocity);
624 }
625 // then use corrected offset to make scroll motion.
626 if (scrollOverCallback_) {
627 scrollOverCallback_(velocity);
628 }
629 }
630
SetSlipFactor(double SlipFactor)631 void Scrollable::SetSlipFactor(double SlipFactor)
632 {
633 slipFactor_ = std::clamp(SlipFactor, -ADJUSTABLE_VELOCITY, ADJUSTABLE_VELOCITY);
634 }
635
GetDefaultOverSpringProperty()636 const RefPtr<SpringProperty>& Scrollable::GetDefaultOverSpringProperty()
637 {
638 return DEFAULT_OVER_SPRING_PROPERTY;
639 }
640
641 } // namespace OHOS::Ace
642