1 /*
2 * Copyright (c) 2023-2024 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_ng/pattern/scrollable/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/utils/time_util.h"
25 #include "base/utils/utils.h"
26 #include "core/common/container.h"
27 #include "base/ressched/ressched_report.h"
28 #include "core/common/layout_inspector.h"
29 #include "core/event/ace_events.h"
30 #include "core/pipeline_ng/pipeline_context.h"
31
32 namespace OHOS::Ace::NG {
33 namespace {
34
35 constexpr double CAP_COEFFICIENT = 0.45;
36 constexpr int32_t FIRST_THRESHOLD = 4;
37 constexpr int32_t SECOND_THRESHOLD = 10;
38 constexpr double CAP_FIXED_VALUE = 16.0;
39 constexpr uint32_t DRAG_INTERVAL_TIME = 900;
40
41 #ifndef WEARABLE_PRODUCT
42 constexpr double FRICTION = 0.6;
43 constexpr double API11_FRICTION = 0.7;
44 constexpr double API12_FRICTION = 0.75;
45 constexpr double VELOCITY_SCALE = 1.0;
46 constexpr double NEW_VELOCITY_SCALE = 1.5;
47 constexpr double ADJUSTABLE_VELOCITY = 3000.0;
48 #else
49 constexpr double DISTANCE_EPSILON = 1.0;
50 constexpr double FRICTION = 0.9;
51 constexpr double VELOCITY_SCALE = 0.8;
52 constexpr double ADJUSTABLE_VELOCITY = 0.0;
53 #endif
54 constexpr float FRICTION_SCALE = -4.2f;
55 constexpr uint32_t CUSTOM_SPRING_ANIMATION_DURATION = 1000;
56 constexpr uint64_t MILLOS_PER_NANO_SECONDS = 1000 * 1000 * 1000;
57 constexpr uint64_t MIN_DIFF_VSYNC = 1000 * 1000; // min is 1ms
58 constexpr float DEFAULT_THRESHOLD = 0.75f;
59 constexpr float DEFAULT_SPRING_RESPONSE = 0.416f;
60 constexpr float DEFAULT_SPRING_DAMP = 0.99f;
61 constexpr uint32_t MAX_VSYNC_DIFF_TIME = 100 * 1000 * 1000; // max 100 ms
62 constexpr float FRICTION_VELOCITY_THRESHOLD = 120.0f;
63 constexpr float SPRING_ACCURACY = 0.1;
64 #ifdef OHOS_PLATFORM
65 constexpr int64_t INCREASE_CPU_TIME_ONCE = 4000000000; // 4s(unit: ns)
66 #endif
67
68 } // namespace
69
70 // Static Functions.
71 std::optional<double> Scrollable::sFriction_ = std::nullopt;
72 std::optional<double> Scrollable::sVelocityScale_ = std::nullopt;
73
SetVelocityScale(double sVelocityScale)74 void Scrollable::SetVelocityScale(double sVelocityScale)
75 {
76 if (LessOrEqual(sVelocityScale, 0.0)) {
77 return;
78 }
79 sVelocityScale_ = sVelocityScale;
80 }
81
GetVelocityScale()82 double Scrollable::GetVelocityScale()
83 {
84 return Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ?
85 NEW_VELOCITY_SCALE : VELOCITY_SCALE;
86 }
87
SetFriction(double sFriction)88 void Scrollable::SetFriction(double sFriction)
89 {
90 if (LessOrEqual(sFriction, 0.0)) {
91 return;
92 }
93 sFriction_ = sFriction;
94 }
95
Scrollable()96 Scrollable::Scrollable()
97 {
98 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
99 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
100 velocityScale_ =
101 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_VELOCITY_SCALE : VELOCITY_SCALE;
102 }
103
Scrollable(ScrollPositionCallback && callback,Axis axis)104 Scrollable::Scrollable(ScrollPositionCallback&& callback, Axis axis) : callback_(std::move(callback)), axis_(axis)
105 {
106 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
107 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
108 velocityScale_ =
109 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_VELOCITY_SCALE : VELOCITY_SCALE;
110 }
111
Scrollable(const ScrollPositionCallback & callback,Axis axis)112 Scrollable::Scrollable(const ScrollPositionCallback& callback, Axis axis) : callback_(callback), axis_(axis)
113 {
114 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
115 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
116 velocityScale_ =
117 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_VELOCITY_SCALE : VELOCITY_SCALE;
118 }
119
~Scrollable()120 Scrollable::~Scrollable()
121 {
122 // If animation still runs, force stop it.
123 StopFrictionAnimation();
124 StopSpringAnimation();
125 StopSnapAnimation();
126 }
127
Initialize(PipelineContext * context)128 void Scrollable::Initialize(PipelineContext* context)
129 {
130 auto weakContext = WeakClaim(context);
131 Initialize(weakContext);
132 }
133
Initialize(const WeakPtr<PipelineBase> & context)134 void Scrollable::Initialize(const WeakPtr<PipelineBase>& context)
135 {
136 context_ = context;
137 PanDirection panDirection;
138 if (axis_ == Axis::VERTICAL) {
139 panDirection.type = PanDirection::VERTICAL;
140 } else {
141 panDirection.type = PanDirection::HORIZONTAL;
142 }
143
144 auto actionStart = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
145 auto scroll = weakScroll.Upgrade();
146 if (scroll) {
147 scroll->isDragging_ = true;
148 scroll->HandleDragStart(info);
149 }
150 };
151
152 auto actionUpdate = [weakScroll = AceType::WeakClaim(this)](const GestureEvent& info) {
153 auto scroll = weakScroll.Upgrade();
154 if (scroll) {
155 scroll->HandleDragUpdate(info);
156 }
157 };
158
159 auto actionEnd = [weakScroll = AceType::WeakClaim(this)](GestureEvent& info) {
160 auto scroll = weakScroll.Upgrade();
161 if (scroll) {
162 scroll->HandleDragEnd(info);
163 if (scroll->panActionEndEvents_.empty()) {
164 scroll->isDragging_ = false;
165 return;
166 }
167 std::for_each(scroll->panActionEndEvents_.begin(), scroll->panActionEndEvents_.end(),
168 [info](GestureEventFunc& event) {
169 auto gestureInfo = info;
170 event(gestureInfo);
171 });
172 scroll->isDragging_ = false;
173 }
174 };
175
176 auto actionCancel = [weakScroll = AceType::WeakClaim(this)]() {
177 auto scroll = weakScroll.Upgrade();
178 if (!scroll) {
179 return;
180 }
181 if (scroll->dragCancelCallback_) {
182 scroll->dragCancelCallback_();
183 }
184 GestureEvent info;
185 scroll->HandleDragEnd(info);
186 if (scroll->panActionEndEvents_.empty()) {
187 scroll->isDragging_ = false;
188 return;
189 }
190 std::for_each(scroll->panActionEndEvents_.begin(), scroll->panActionEndEvents_.end(),
191 [info](GestureEventFunc& event) {
192 auto gestureInfo = info;
193 event(gestureInfo);
194 });
195 scroll->isDragging_ = false;
196 };
197
198 if (Container::IsCurrentUseNewPipeline()) {
199 panRecognizerNG_ = AceType::MakeRefPtr<NG::PanRecognizer>(
200 DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
201 panRecognizerNG_->SetIsAllowMouse(false);
202 panRecognizerNG_->SetOnActionStart(actionStart);
203 panRecognizerNG_->SetOnActionUpdate(actionUpdate);
204 panRecognizerNG_->SetOnActionEnd(actionEnd);
205 panRecognizerNG_->SetOnActionCancel(actionCancel);
206 }
207 available_ = true;
208 }
209
SetAxis(Axis axis)210 void Scrollable::SetAxis(Axis axis)
211 {
212 axis_ = axis;
213 PanDirection panDirection;
214 if (axis_ == Axis::NONE) {
215 panDirection.type = PanDirection::NONE;
216 } else if (axis_ == Axis::VERTICAL) {
217 panDirection.type = PanDirection::VERTICAL;
218 } else {
219 panDirection.type = PanDirection::HORIZONTAL;
220 }
221 if (panRecognizerNG_) {
222 panRecognizerNG_->SetDirection(panDirection);
223 }
224 }
225
HandleTouchDown()226 void Scrollable::HandleTouchDown()
227 {
228 isTouching_ = true;
229 // If animation still runs, first stop it.
230 ACE_SCOPED_TRACE("HandleTouchDown, panDirection:%u, id:%d, tag:%s", GetPanDirection(), nodeId_, nodeTag_.c_str());
231 StopSpringAnimation();
232 if (!isFrictionAnimationStop_) {
233 StopFrictionAnimation();
234 } else if (!isSnapAnimationStop_ || !isSnapScrollAnimationStop_) {
235 StopSnapAnimation();
236 } else {
237 // Resets values.
238 currentPos_ = 0.0;
239 }
240 }
241
HandleTouchUp()242 void Scrollable::HandleTouchUp()
243 {
244 // Two fingers are alternately drag, one finger is released without triggering spring animation.
245 ACE_SCOPED_TRACE("HandleTouchUp, isDragging_:%u, nestedScrolling_:%u id:%d, tag:%s",
246 isDragging_, nestedScrolling_, nodeId_, nodeTag_.c_str());
247 if (isDragging_) {
248 return;
249 }
250 isTouching_ = false;
251 if (nestedScrolling_) {
252 return;
253 }
254 // outBoundaryCallback_ is only set in ScrollablePattern::SetEdgeEffect and when the edge effect is spring
255 if (outBoundaryCallback_ && outBoundaryCallback_()) {
256 if (isSpringAnimationStop_ && scrollOverCallback_) {
257 if (onScrollStartRec_) {
258 onScrollStartRec_(static_cast<float>(axis_));
259 }
260 ProcessScrollOverCallback(0.0);
261 }
262 return;
263 }
264 if (isSnapScrollAnimationStop_ && scrollSnapCallback_) {
265 scrollSnapCallback_(0.0, 0.0);
266 }
267 }
268
HandleTouchCancel()269 void Scrollable::HandleTouchCancel()
270 {
271 isTouching_ = false;
272 ACE_SCOPED_TRACE("HandleTouchCancel, id:%d, tag:%s", nodeId_, nodeTag_.c_str());
273 if (isSpringAnimationStop_ && scrollOverCallback_) {
274 ProcessScrollOverCallback(0.0);
275 }
276 }
277
IsAnimationNotRunning() const278 bool Scrollable::IsAnimationNotRunning() const
279 {
280 return !isTouching_ && isFrictionAnimationStop_ && isSpringAnimationStop_
281 && isSnapAnimationStop_ && isSnapScrollAnimationStop_;
282 }
283
Idle() const284 bool Scrollable::Idle() const
285 {
286 return !isTouching_ && isFrictionAnimationStop_ && isSpringAnimationStop_
287 && isSnapAnimationStop_ && isSnapScrollAnimationStop_ && !nestedScrolling_;
288 }
289
IsStopped() const290 bool Scrollable::IsStopped() const
291 {
292 return isSpringAnimationStop_ && isFrictionAnimationStop_
293 && isSnapAnimationStop_ && isSnapScrollAnimationStop_;
294 }
295
IsSpringStopped() const296 bool Scrollable::IsSpringStopped() const
297 {
298 return isSpringAnimationStop_;
299 }
300
IsSnapStopped() const301 bool Scrollable::IsSnapStopped() const
302 {
303 return isSnapAnimationStop_;
304 }
305
StopScrollable()306 void Scrollable::StopScrollable()
307 {
308 if (!isFrictionAnimationStop_) {
309 StopFrictionAnimation();
310 }
311 if (!isSpringAnimationStop_) {
312 StopSpringAnimation();
313 }
314 if (!isSnapAnimationStop_ || !isSnapScrollAnimationStop_) {
315 StopSnapAnimation();
316 }
317 }
318
HandleScrollEnd(const std::optional<float> & velocity)319 void Scrollable::HandleScrollEnd(const std::optional<float>& velocity)
320 {
321 // priority:
322 // 1. onScrollEndRec_ (would internally call onScrollEnd)
323 // 2. scrollEndCallback_
324 if (onScrollEndRec_) {
325 onScrollEndRec_(velocity);
326 return;
327 }
328 if (scrollEndCallback_) {
329 scrollEndCallback_();
330 }
331 }
332
HandleDragStart(const OHOS::Ace::GestureEvent & info)333 void Scrollable::HandleDragStart(const OHOS::Ace::GestureEvent& info)
334 {
335 ACE_SCOPED_TRACE("HandleDragStart, id:%d, tag:%s", nodeId_, nodeTag_.c_str());
336 if (info.GetSourceTool() == SourceTool::TOUCHPAD) {
337 HandleTouchDown();
338 }
339 currentVelocity_ = info.GetMainVelocity();
340 if (dragFRCSceneCallback_) {
341 dragFRCSceneCallback_(currentVelocity_, NG::SceneStatus::START);
342 }
343 if (continuousDragStatus_) {
344 IncreaseContinueDragCount();
345 task_.Cancel();
346 }
347 SetDragStartPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
348 const double dragPositionInMainAxis =
349 axis_ == Axis::VERTICAL ? info.GetGlobalLocation().GetY() : info.GetGlobalLocation().GetX();
350 TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Scroll drag start, id:%{public}d, tag:%{public}s", nodeId_, nodeTag_.c_str());
351
352 skipRestartSpring_ = false; // reset flags. Extract method if more flags need to be reset
353
354 #ifdef OHOS_PLATFORM
355 // Increase the cpu frequency when sliding start.
356 auto currentTime = GetSysTimestamp();
357 auto increaseCpuTime = currentTime - startIncreaseTime_;
358 if (!moved_ || increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
359 startIncreaseTime_ = currentTime;
360 if (FrameReport::GetInstance().GetEnable()) {
361 FrameReport::GetInstance().BeginListFling();
362 }
363 }
364 #endif
365 JankFrameReport::GetInstance().SetFrameJankFlag(JANK_RUNNING_SCROLL);
366 if (onScrollStartRec_) {
367 onScrollStartRec_(static_cast<float>(dragPositionInMainAxis));
368 }
369 }
370
HandleScroll(double offset,int32_t source,NestedState state)371 ScrollResult Scrollable::HandleScroll(double offset, int32_t source, NestedState state)
372 {
373 if (!handleScrollCallback_) {
374 ExecuteScrollBegin(offset);
375 canOverScroll_ = false;
376 moved_ = UpdateScrollPosition(offset, source);
377 return { 0, false };
378 }
379 // call NestableScrollContainer::HandleScroll
380 return handleScrollCallback_(static_cast<float>(offset), source, state);
381 }
382
HandleDragUpdate(const GestureEvent & info)383 void Scrollable::HandleDragUpdate(const GestureEvent& info)
384 {
385 currentVelocity_ = info.GetMainVelocity();
386 if (dragFRCSceneCallback_) {
387 dragFRCSceneCallback_(currentVelocity_, NG::SceneStatus::RUNNING);
388 }
389 if (!NearZero(info.GetMainVelocity()) && dragCount_ >= FIRST_THRESHOLD) {
390 if (Negative(lastVelocity_ / info.GetMainVelocity())) {
391 ResetContinueDragCount();
392 }
393 }
394 if (!isSpringAnimationStop_ || !isFrictionAnimationStop_ ||
395 !isSnapAnimationStop_ || !isSnapScrollAnimationStop_) {
396 // If animation still runs, first stop it.
397 isDragUpdateStop_ = true;
398 StopFrictionAnimation();
399 StopSpringAnimation();
400 StopSnapAnimation();
401 currentPos_ = 0.0;
402 }
403 #ifdef OHOS_PLATFORM
404 // Handle the case where you keep sliding past limit time(4s).
405 auto currentTime = GetSysTimestamp();
406 auto increaseCpuTime = currentTime - startIncreaseTime_;
407 if (increaseCpuTime >= INCREASE_CPU_TIME_ONCE) {
408 startIncreaseTime_ = currentTime;
409 if (FrameReport::GetInstance().GetEnable()) {
410 FrameReport::GetInstance().BeginListFling();
411 }
412 }
413 #endif
414 auto mainDelta = info.GetMainDelta();
415 auto source = IsMouseWheelScroll(info) ? SCROLL_FROM_AXIS : SCROLL_FROM_UPDATE;
416 ACE_SCOPED_TRACE(
417 "HandleDragUpdate, mainDelta:%f, source:%d, id:%d, tag:%s", mainDelta, source, nodeId_, nodeTag_.c_str());
418 if (isReverseCallback_ && isReverseCallback_()) {
419 mainDelta = Round(-mainDelta);
420 } else {
421 mainDelta = Round(mainDelta);
422 }
423 JankFrameReport::GetInstance().RecordFrameUpdate();
424 HandleScroll(mainDelta, source, NestedState::GESTURE);
425 }
426
LayoutDirectionEst(double & correctVelocity)427 void Scrollable::LayoutDirectionEst(double& correctVelocity)
428 {
429 if (isReverseCallback_ && isReverseCallback_()) {
430 correctVelocity = -correctVelocity * sVelocityScale_.value_or(velocityScale_) * GetGain(GetDragOffset());
431 } else {
432 correctVelocity = correctVelocity * sVelocityScale_.value_or(velocityScale_) * GetGain(GetDragOffset());
433 }
434 }
435
HandleDragEnd(const GestureEvent & info)436 void Scrollable::HandleDragEnd(const GestureEvent& info)
437 {
438 // avoid no render frame when drag end
439 HandleDragUpdate(info);
440
441 TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Scroll drag end, velocity is %{public}f id:%{public}d, tag:%{public}s",
442 info.GetMainVelocity(), nodeId_, nodeTag_.c_str());
443 if (dragFRCSceneCallback_) {
444 dragFRCSceneCallback_(info.GetMainVelocity(), NG::SceneStatus::END);
445 }
446 isDragUpdateStop_ = false;
447 touchUp_ = false;
448 scrollPause_ = false;
449 lastVelocity_ = GetPanDirection() == Axis::NONE ? 0.0 : info.GetMainVelocity();
450 double gestureVelocity = GetPanDirection() == Axis::NONE ? 0.0 : info.GetMainVelocity();
451 SetDragEndPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
452 LayoutDirectionEst(gestureVelocity);
453 // Apply max fling velocity limit, it must be calculated after all fling velocity gain.
454 currentVelocity_ = std::clamp(gestureVelocity, -maxFlingVelocity_ + slipFactor_, maxFlingVelocity_ - slipFactor_);
455
456 lastPos_ = GetDragOffset();
457 JankFrameReport::GetInstance().ClearFrameJankFlag(JANK_RUNNING_SCROLL);
458 double mainPosition = GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY()));
459 mainPosition = Round(mainPosition);
460 ACE_SCOPED_TRACE("HandleDragEnd, mainPosition:%f, gestureVelocity:%f, currentVelocity:%f, moved_:%u "
461 "canOverScroll_:%u, id:%d, tag:%s",
462 mainPosition, gestureVelocity, currentVelocity_, moved_, canOverScroll_, nodeId_, nodeTag_.c_str());
463 if (!moved_ || IsMouseWheelScroll(info)) {
464 if (calePredictSnapOffsetCallback_) {
465 std::optional<float> predictSnapOffset = calePredictSnapOffsetCallback_(0.0f, 0.0f, 0.0f);
466 if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
467 currentPos_ = mainPosition;
468 ProcessScrollSnapSpringMotion(predictSnapOffset.value(), currentVelocity_);
469 isTouching_ = false;
470 return;
471 }
472 }
473 HandleScrollEnd(currentVelocity_);
474 currentVelocity_ = 0.0;
475 #ifdef OHOS_PLATFORM
476 if (FrameReport::GetInstance().GetEnable()) {
477 FrameReport::GetInstance().EndListFling();
478 }
479 #endif
480 } else if (!Container::IsCurrentUseNewPipeline() && outBoundaryCallback_ && outBoundaryCallback_() &&
481 scrollOverCallback_) {
482 ResetContinueDragCount();
483 ProcessScrollOverCallback(currentVelocity_);
484 } else if (canOverScroll_) {
485 ResetContinueDragCount();
486 HandleOverScroll(currentVelocity_);
487 } else {
488 StartScrollAnimation(mainPosition, currentVelocity_);
489 }
490 SetDelayedTask();
491 if (dragEndCallback_) {
492 dragEndCallback_();
493 }
494 isTouching_ = false;
495 }
496
StartScrollAnimation(float mainPosition,float correctVelocity)497 void Scrollable::StartScrollAnimation(float mainPosition, float correctVelocity)
498 {
499 if (!isSpringAnimationStop_) {
500 StopSpringAnimation();
501 }
502 if (!frictionOffsetProperty_) {
503 GetFrictionProperty();
504 }
505 StopSnapController();
506 TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "The position of scroll motion is %{public}f, velocity is %{public}f",
507 mainPosition, correctVelocity);
508 float friction = sFriction_.value_or(friction_);
509 initVelocity_ = correctVelocity;
510 finalPosition_ = mainPosition + correctVelocity / (friction * -FRICTION_SCALE);
511
512 if (calePredictSnapOffsetCallback_) {
513 std::optional<float> predictSnapOffset =
514 calePredictSnapOffsetCallback_(GetFinalPosition() - mainPosition, GetDragOffset(), correctVelocity);
515 if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
516 currentPos_ = mainPosition;
517 ProcessScrollSnapSpringMotion(predictSnapOffset.value(), correctVelocity);
518 return;
519 }
520 }
521 if (scrollSnapCallback_ && scrollSnapCallback_(GetFinalPosition() - mainPosition, correctVelocity)) {
522 currentVelocity_ = 0.0;
523 return;
524 }
525 if (NearZero(correctVelocity, FRICTION_VELOCITY_THRESHOLD)) {
526 HandleScrollEnd(correctVelocity);
527 currentVelocity_ = 0.0;
528 #ifdef OHOS_PLATFORM
529 if (FrameReport::GetInstance().GetEnable()) {
530 FrameReport::GetInstance().EndListFling();
531 }
532 #endif
533 return;
534 }
535 // change motion param when list item need to be center of screen on watch
536 FixScrollMotion(mainPosition, correctVelocity);
537 // Resets values.
538 currentPos_ = mainPosition;
539 currentVelocity_ = 0.0;
540 lastPosition_ = currentPos_;
541 frictionVelocity_ = initVelocity_;
542 frictionOffsetProperty_->Set(mainPosition);
543 float response = fabs(2 * M_PI / (FRICTION_SCALE * friction));
544 auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(response, 1.0f, 0.0f);
545 AnimationOption option;
546 option.SetCurve(curve);
547 option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
548 option.SetFinishCallbackType(FinishCallbackType::LOGICALLY);
549 frictionOffsetProperty_->SetThresholdType(ThresholdType::LAYOUT);
550 frictionOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
551 ACE_SCOPED_TRACE("Scrollable friction animation start, start:%f, end:%f, vel:%f, id:%d, tag:%s", mainPosition,
552 finalPosition_, initVelocity_, nodeId_, nodeTag_.c_str());
553 frictionOffsetProperty_->AnimateWithVelocity(
554 option, finalPosition_, initVelocity_, [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
555 ContainerScope scope(id);
556 auto scroll = weak.Upgrade();
557 CHECK_NULL_VOID(scroll);
558 scroll->isFrictionAnimationStop_ = true;
559 ACE_SCOPED_TRACE(
560 "Scrollable friction animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
561 scroll->ProcessScrollMotionStop(true);
562 });
563 isFrictionAnimationStop_ = false;
564 auto context = context_.Upgrade();
565 CHECK_NULL_VOID(context);
566 context->RequestFrame();
567 lastVsyncTime_ = context->GetVsyncTime();
568 }
569
SetDelayedTask()570 void Scrollable::SetDelayedTask()
571 {
572 SetContinuousDragStatus(true);
573 auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
574 CHECK_NULL_VOID(context);
575 auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
576 task_.Reset([weak = WeakClaim(this)] {
577 auto drag = weak.Upgrade();
578 if (drag) {
579 drag->ResetContinueDragCount();
580 drag->SetContinuousDragStatus(false);
581 }
582 });
583 taskExecutor.PostDelayedTask(task_, DRAG_INTERVAL_TIME, "ArkUIScrollDragInterval");
584 }
585
MarkNeedFlushAnimationStartTime()586 void Scrollable::MarkNeedFlushAnimationStartTime()
587 {
588 auto context = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
589 CHECK_NULL_VOID(context);
590 context->MarkNeedFlushAnimationStartTime();
591 }
592
ComputeCap(int dragCount)593 double Scrollable::ComputeCap(int dragCount)
594 {
595 if (dragCount < FIRST_THRESHOLD) {
596 return 1.0;
597 }
598 auto cap = ComputeCap(dragCount - 1) + CAP_COEFFICIENT * (dragCount - 1);
599 return cap;
600 }
601
GetGain(double delta)602 double Scrollable::GetGain(double delta)
603 {
604 auto cap = 1.0;
605 auto gain = 1.0;
606 if (!continuousSlidingCallback_) {
607 preGain_ = gain;
608 return gain;
609 }
610 auto screenHeight = continuousSlidingCallback_();
611 if (delta == 0 || screenHeight == 0) {
612 preGain_ = gain;
613 return gain;
614 }
615 if (dragCount_ >= FIRST_THRESHOLD && dragCount_ < SECOND_THRESHOLD) {
616 if (Negative(lastPos_ / delta)) {
617 ResetContinueDragCount();
618 preGain_ = gain;
619 return gain;
620 }
621 cap = CAP_COEFFICIENT * (dragCount_ - 1);
622 gain = (LessNotEqual(cap, std::abs(delta) / screenHeight * (dragCount_ - 1))) ? preGain_ + cap :
623 preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1);
624 } else if (dragCount_ >= SECOND_THRESHOLD) {
625 if (Negative(lastPos_ / delta)) {
626 ResetContinueDragCount();
627 preGain_ = gain;
628 return gain;
629 }
630 cap = CAP_FIXED_VALUE;
631 gain = (LessNotEqual(cap, preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1))) ? cap :
632 preGain_ + std::abs(delta) / screenHeight * (dragCount_ - 1);
633 }
634 preGain_ = gain;
635 return gain;
636 }
637
ExecuteScrollBegin(double & mainDelta)638 void Scrollable::ExecuteScrollBegin(double& mainDelta)
639 {
640 auto context = context_.Upgrade();
641 if (!scrollBeginCallback_ || !context) {
642 return;
643 }
644
645 ScrollInfo scrollInfo;
646 if (axis_ == Axis::VERTICAL) {
647 scrollInfo = scrollBeginCallback_(0.0_vp, Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP));
648 mainDelta = context->NormalizeToPx(scrollInfo.dy);
649 } else if (axis_ == Axis::HORIZONTAL) {
650 scrollInfo = scrollBeginCallback_(Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP), 0.0_vp);
651 mainDelta = context->NormalizeToPx(scrollInfo.dx);
652 }
653 }
654
GetFrictionVelocityByFinalPosition(float final,float position,float friction,float signum,float threshold)655 float Scrollable::GetFrictionVelocityByFinalPosition(float final, float position, float friction,
656 float signum, float threshold)
657 {
658 return DEFAULT_THRESHOLD * threshold * signum - (final - position) * friction;
659 }
660
FixScrollMotion(float position,float initVelocity)661 void Scrollable::FixScrollMotion(float position, float initVelocity)
662 {
663 #ifdef WEARABLE_PRODUCT
664 float signum = 0.0;
665 if (!NearZero(initVelocity)) {
666 signum = GreatNotEqual(initVelocity, 0.0) ? 1.0 : -1.0;
667 }
668 if (frictionOffsetProperty_ && needCenterFix_ && watchFixCallback_) {
669 float finalPosition = watchFixCallback_(GetFinalPosition(), position);
670 if (!NearEqual(finalPosition, GetFinalPosition(), DISTANCE_EPSILON)) {
671 float friction = sFriction_.value_or(friction_);
672 float velocity = GetFrictionVelocityByFinalPosition(finalPosition, position, friction, signum);
673
674 // fix again when velocity is less than velocity threshold
675 if (!NearEqual(finalPosition, GetFinalPosition(), DISTANCE_EPSILON)) {
676 velocity = GetFrictionVelocityByFinalPosition(finalPosition, position, friction, signum, 0.0f);
677 }
678 initVelocity_ = velocity;
679 finalPosition_ = mainPosition + initVelocity_ / (friction * -FRICTION_SCALE);
680 }
681 }
682 #endif
683 }
684
StartScrollSnapMotion(float predictSnapOffset,float scrollSnapVelocity)685 void Scrollable::StartScrollSnapMotion(float predictSnapOffset, float scrollSnapVelocity)
686 {
687 endPos_ = currentPos_ + predictSnapOffset;
688 AnimationOption option;
689 option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
690 auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
691 option.SetCurve(curve);
692 if (!snapOffsetProperty_) {
693 GetSnapProperty();
694 }
695 snapOffsetProperty_->Set(currentPos_);
696 snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
697 ACE_SCOPED_TRACE("List snap animation start, start:%f, end:%f, vel:%f, id:%d", currentPos_, endPos_,
698 scrollSnapVelocity, nodeId_);
699 updateSnapAnimationCount_++;
700 snapOffsetProperty_->AnimateWithVelocity(option, endPos_, scrollSnapVelocity,
701 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
702 ContainerScope scope(id);
703 auto scroll = weak.Upgrade();
704 CHECK_NULL_VOID(scroll);
705 ACE_SCOPED_TRACE("List snap animation finish, id:%d", scroll->nodeId_);
706 scroll->updateSnapAnimationCount_--;
707 if (scroll->updateSnapAnimationCount_ == 0) {
708 scroll->isSnapScrollAnimationStop_ = true;
709 scroll->ProcessScrollSnapStop();
710 }
711 });
712 isSnapScrollAnimationStop_ = false;
713 auto context = context_.Upgrade();
714 CHECK_NULL_VOID(context);
715 lastVsyncTime_ = context->GetVsyncTime();
716 }
717
ProcessScrollSnapSpringMotion(float scrollSnapDelta,float scrollSnapVelocity)718 void Scrollable::ProcessScrollSnapSpringMotion(float scrollSnapDelta, float scrollSnapVelocity)
719 {
720 TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "The snap delta of scroll motion is %{public}f, "
721 "The snap velocity of scroll motion is %{public}f",
722 scrollSnapDelta, scrollSnapVelocity);
723 endPos_ = currentPos_ + scrollSnapDelta;
724 ACE_SCOPED_TRACE("Scroll snap animation start, start:%f, end:%f, vel:%f, id:%d", currentPos_, endPos_,
725 scrollSnapVelocity, nodeId_);
726 AnimationOption option;
727 option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
728 auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
729 option.SetCurve(curve);
730 if (!snapOffsetProperty_) {
731 GetSnapProperty();
732 }
733 snapOffsetProperty_->Set(currentPos_);
734 snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
735 snapOffsetProperty_->AnimateWithVelocity(option, endPos_, scrollSnapVelocity,
736 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
737 ContainerScope scope(id);
738 auto scroll = weak.Upgrade();
739 CHECK_NULL_VOID(scroll);
740 scroll->isSnapAnimationStop_ = true;
741 ACE_SCOPED_TRACE("Scroll snap animation finish, id:%d", scroll->nodeId_);
742 scroll->ProcessScrollMotionStop(false);
743 });
744 isSnapAnimationStop_ = false;
745 auto context = context_.Upgrade();
746 CHECK_NULL_VOID(context);
747 lastVsyncTime_ = context->GetVsyncTime();
748 }
749
UpdateScrollSnapStartOffset(double offset)750 void Scrollable::UpdateScrollSnapStartOffset(double offset)
751 {
752 UpdateScrollSnapEndWithOffset(offset);
753 }
754
ProcessScrollSnapMotion(double position)755 void Scrollable::ProcessScrollSnapMotion(double position)
756 {
757 TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "Current Pos is %{public}f, position is %{public}f",
758 currentPos_, position);
759 currentVelocity_ = snapVelocity_;
760 if (NearEqual(currentPos_, position)) {
761 UpdateScrollPosition(0.0, SCROLL_FROM_ANIMATION);
762 } else {
763 auto mainDelta = position - currentPos_;
764 HandleScroll(mainDelta, SCROLL_FROM_ANIMATION, NestedState::GESTURE);
765 if (!moved_ && !isSnapScrollAnimationStop_) {
766 StopSnapAnimation();
767 } else if (!touchUp_) {
768 if (scrollTouchUpCallback_) {
769 scrollTouchUpCallback_();
770 }
771 touchUp_ = true;
772 }
773 }
774 currentPos_ = position;
775 if (outBoundaryCallback_ && outBoundaryCallback_() && !isSnapScrollAnimationStop_) {
776 scrollPause_ = true;
777 skipRestartSpring_ = true;
778 MarkNeedFlushAnimationStartTime();
779 StopSnapAnimation();
780 }
781 }
782
ProcessScrollSnapStop()783 void Scrollable::ProcessScrollSnapStop()
784 {
785 if (scrollPause_) {
786 scrollPause_ = false;
787 HandleOverScroll(currentVelocity_);
788 } else {
789 OnAnimateStop();
790 }
791 }
792
OnAnimateStop()793 void Scrollable::OnAnimateStop()
794 {
795 HandleScrollEnd(std::nullopt);
796 currentVelocity_ = 0.0;
797 if (isTouching_ || isDragUpdateStop_) {
798 return;
799 }
800 moved_ = false;
801 #ifdef OHOS_PLATFORM
802 if (FrameReport::GetInstance().GetEnable()) {
803 FrameReport::GetInstance().EndListFling();
804 }
805 #endif
806 if (scrollEnd_) {
807 scrollEnd_();
808 }
809 #if !defined(PREVIEW)
810 LayoutInspector::SupportInspector();
811 #endif
812 }
813
StartSpringMotion(double mainPosition,double mainVelocity,const ExtentPair & extent,const ExtentPair & initExtent)814 void Scrollable::StartSpringMotion(
815 double mainPosition, double mainVelocity, const ExtentPair& extent, const ExtentPair& initExtent)
816 {
817 TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "position is %{public}f, mainVelocity is %{public}f, minExtent is "
818 "%{public}f, maxExtent is %{public}f, initMinExtent is %{public}f, initMaxExtent is %{public}f",
819 mainPosition, mainVelocity, extent.Leading(), extent.Trailing(), initExtent.Leading(), initExtent.Trailing());
820 if (!isSpringAnimationStop_ || (skipRestartSpring_ && NearEqual(mainVelocity, 0.0f, 0.001f))) {
821 return;
822 }
823 currentPos_ = mainPosition;
824 if (mainPosition > initExtent.Trailing() || NearEqual(mainPosition, initExtent.Trailing(), 0.01f)) {
825 finalPosition_ = extent.Trailing();
826 } else if (mainPosition < initExtent.Leading() || NearEqual(mainPosition, initExtent.Leading(), 0.01f)) {
827 finalPosition_ = extent.Leading();
828 } else {
829 return;
830 }
831
832 if (!springOffsetProperty_) {
833 GetSpringProperty();
834 }
835 springAnimationCount_++;
836 springOffsetProperty_->Set(mainPosition);
837 AnimationOption option;
838 auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
839 option.SetCurve(curve);
840 option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
841 springOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
842 ACE_SCOPED_TRACE("Scrollable spring animation start, start:%f, end:%f, vel:%f, id:%d, tag:%s", mainPosition,
843 finalPosition_, mainVelocity, nodeId_, nodeTag_.c_str());
844 lastVsyncTime_ = static_cast<uint64_t>(GetSysTimestamp());
845 springOffsetProperty_->AnimateWithVelocity(
846 option, finalPosition_, mainVelocity, [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
847 ContainerScope scope(id);
848 auto scroll = weak.Upgrade();
849 CHECK_NULL_VOID(scroll);
850 scroll->springAnimationCount_--;
851 ACE_SCOPED_TRACE(
852 "Scrollable spring animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
853 // avoid current animation being interrupted by the prev animation's finish callback
854 // and triggering onScrollStop when spring animation turns to friction animation.
855 if (scroll->springAnimationCount_ > 0 || scroll->scrollPause_) {
856 return;
857 }
858 scroll->isSpringAnimationStop_ = true;
859 scroll->currentVelocity_ = 0.0;
860 scroll->OnAnimateStop();
861 });
862 isSpringAnimationStop_ = false;
863 skipRestartSpring_ = false;
864 auto context = context_.Upgrade();
865 CHECK_NULL_VOID(context);
866 context->RequestFrame();
867 }
868
UpdateSpringMotion(double mainPosition,const ExtentPair & extent,const ExtentPair & initExtent)869 void Scrollable::UpdateSpringMotion(
870 double mainPosition, const ExtentPair& extent, const ExtentPair& initExtent)
871 {
872 TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "position is %{public}f, minExtent is "
873 "%{public}f, maxExtent is %{public}f, initMinExtent is %{public}f, initMaxExtent is %{public}f",
874 mainPosition, extent.Leading(), extent.Trailing(), initExtent.Leading(), initExtent.Trailing());
875 if (isSpringAnimationStop_) {
876 return;
877 }
878 float finalPosition = 0.0f;
879 if (mainPosition > initExtent.Trailing() || NearEqual(mainPosition, initExtent.Trailing())) {
880 finalPosition = extent.Trailing();
881 } else if (mainPosition < initExtent.Leading() || NearEqual(mainPosition, initExtent.Leading())) {
882 finalPosition = extent.Leading();
883 } else {
884 return;
885 }
886
887 finalPosition = finalPosition_ + (finalPosition - mainPosition) - (finalPosition_ - currentPos_);
888 if (NearEqual(finalPosition, finalPosition_, SPRING_ACCURACY)) {
889 return;
890 }
891 finalPosition_ = finalPosition;
892 springAnimationCount_++;
893 AnimationOption option;
894 auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
895 option.SetCurve(curve);
896 option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
897 springOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
898 ACE_SCOPED_TRACE("Scrollable spring animation update, start:%f, end:%f, id:%d, tag:%s", mainPosition,
899 finalPosition_, nodeId_, nodeTag_.c_str());
900 AnimationUtils::StartAnimation(
901 option,
902 [weak = AceType::WeakClaim(this)]() {
903 auto scroll = weak.Upgrade();
904 CHECK_NULL_VOID(scroll);
905 scroll->springOffsetProperty_->Set(scroll->finalPosition_);
906 scroll->isSpringAnimationStop_ = false;
907 },
908 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
909 ContainerScope scope(id);
910 auto scroll = weak.Upgrade();
911 CHECK_NULL_VOID(scroll);
912 scroll->springAnimationCount_--;
913 // avoid current animation being interrupted by the prev animation's finish callback
914 if (scroll->springAnimationCount_ > 0) {
915 return;
916 }
917 ACE_SCOPED_TRACE(
918 "Scrollable updated spring animation finish, id:%d, tag:%s", scroll->nodeId_, scroll->nodeTag_.c_str());
919 scroll->isSpringAnimationStop_ = true;
920 scroll->currentVelocity_ = 0.0;
921 scroll->OnAnimateStop();
922 });
923 isSpringAnimationStop_ = false;
924 skipRestartSpring_ = false;
925 }
926
ProcessScrollMotionStop(bool stopFriction)927 void Scrollable::ProcessScrollMotionStop(bool stopFriction)
928 {
929 if (needScrollSnapChange_ && calePredictSnapOffsetCallback_ && frictionOffsetProperty_) {
930 needScrollSnapChange_ = false;
931 auto predictSnapOffset = calePredictSnapOffsetCallback_(GetFinalPosition() - currentPos_, 0.0f, 0.0f);
932 if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
933 ProcessScrollSnapSpringMotion(predictSnapOffset.value(), currentVelocity_);
934 return;
935 }
936 }
937 // spring effect special process
938 if (scrollPause_) {
939 scrollPause_ = false;
940 HandleOverScroll(currentVelocity_);
941 } else {
942 if (isDragUpdateStop_) {
943 return;
944 }
945 moved_ = false;
946 HandleScrollEnd(std::nullopt);
947 #ifdef OHOS_PLATFORM
948 if (FrameReport::GetInstance().GetEnable()) {
949 FrameReport::GetInstance().EndListFling();
950 }
951 #endif
952 if (scrollEnd_) {
953 scrollEnd_();
954 }
955 currentVelocity_ = 0.0;
956 #if !defined(PREVIEW)
957 LayoutInspector::SupportInspector();
958 #endif
959 }
960 }
961
ProcessSpringMotion(double position)962 void Scrollable::ProcessSpringMotion(double position)
963 {
964 TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "Current Pos is %{public}f, position is %{public}f",
965 currentPos_, position);
966 auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
967 CHECK_NULL_VOID(context);
968 uint64_t currentVsync = context->GetVsyncTime();
969 uint64_t diff = currentVsync - lastVsyncTime_;
970 if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
971 currentVelocity_ = (position - currentPos_) / diff * MILLOS_PER_NANO_SECONDS;
972 }
973 lastVsyncTime_ = currentVsync;
974 if (LessOrEqual(std::abs(currentPos_ - position), 1)) {
975 // trace stop at OnScrollStop
976 if (!isFadingAway_) {
977 AceAsyncTraceBegin(0, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") + nodeTag_).c_str());
978 }
979 }
980 auto distance = currentPos_ - finalPosition_;
981 auto nextDistance = position - finalPosition_;
982 isFadingAway_ = GreatNotEqual(std::abs(nextDistance), std::abs(distance));
983 auto delta = position - currentPos_;
984 if (distance * nextDistance < 0) {
985 double currentVelocity = currentVelocity_;
986 scrollPause_ = true;
987 MarkNeedFlushAnimationStartTime();
988 StopSpringAnimation();
989 ACE_SCOPED_TRACE("change direction in spring animation and start fling animation, distance:%f, "
990 "nextDistance:%f, nodeId:%d, tag:%s",
991 distance, nextDistance, nodeId_, nodeTag_.c_str());
992 // only handle offsets that are out of bounds
993 delta = finalPosition_ - currentPos_;
994 // remainVelocityCallback_ will pass the velocity to the child component
995 if (!remainVelocityCallback_ || !remainVelocityCallback_(currentVelocity)) {
996 StartScrollAnimation(position, currentVelocity);
997 }
998 }
999 moved_ = UpdateScrollPosition(delta, SCROLL_FROM_ANIMATION_SPRING);
1000 if (!moved_) {
1001 StopSpringAnimation();
1002 } else if (!touchUp_) {
1003 if (scrollTouchUpCallback_) {
1004 scrollTouchUpCallback_();
1005 }
1006 touchUp_ = true;
1007 }
1008 currentPos_ = position;
1009 }
1010
ProcessScrollMotion(double position)1011 void Scrollable::ProcessScrollMotion(double position)
1012 {
1013 currentVelocity_ = frictionVelocity_;
1014 if (needScrollSnapToSideCallback_) {
1015 needScrollSnapChange_ = needScrollSnapToSideCallback_(position - currentPos_);
1016 }
1017 TAG_LOGD(AceLogTag::ACE_SCROLLABLE, "position is %{public}f, currentVelocity_ is %{public}f, "
1018 "needScrollSnapChange_ is %{public}u",
1019 position, currentVelocity_, needScrollSnapChange_);
1020 if (LessOrEqual(std::abs(currentPos_ - position), 1)) {
1021 // trace stop at OnScrollStop
1022 AceAsyncTraceBegin(0, (TRAILING_ANIMATION + std::to_string(nodeId_) + std::string(" ") + nodeTag_).c_str());
1023 }
1024 // UpdateScrollPosition return false, means reach to scroll limit.
1025 auto mainDelta = position - currentPos_;
1026 HandleScroll(mainDelta, SCROLL_FROM_ANIMATION, NestedState::GESTURE);
1027 if (!moved_) {
1028 StopFrictionAnimation();
1029 } else if (!touchUp_) {
1030 if (scrollTouchUpCallback_) {
1031 scrollTouchUpCallback_();
1032 }
1033 touchUp_ = true;
1034 }
1035 currentPos_ = position;
1036
1037 // spring effect special process
1038 if ((IsSnapStopped() && canOverScroll_) || needScrollSnapChange_ ||
1039 (!Container::IsCurrentUseNewPipeline() && outBoundaryCallback_ && outBoundaryCallback_())) {
1040 ACE_SCOPED_TRACE("scrollPause set true to stop ProcessScrollMotion, canOverScroll:%u, needScrollSnapChange:%u, "
1041 "nodeId:%d, tag:%s", canOverScroll_, needScrollSnapChange_, nodeId_, nodeTag_.c_str());
1042 scrollPause_ = true;
1043 skipRestartSpring_ = true;
1044 MarkNeedFlushAnimationStartTime();
1045 StopFrictionAnimation();
1046 }
1047 }
1048
UpdateScrollPosition(const double offset,int32_t source) const1049 bool Scrollable::UpdateScrollPosition(const double offset, int32_t source) const
1050 {
1051 bool ret = true;
1052 if (callback_) {
1053 ret = callback_(offset, source);
1054 }
1055 return ret;
1056 }
1057
ProcessScrollOverCallback(double velocity)1058 void Scrollable::ProcessScrollOverCallback(double velocity)
1059 {
1060 if (outBoundaryCallback_ && !outBoundaryCallback_() && !canOverScroll_) {
1061 return;
1062 }
1063 // In the case of chain animation enabled, you need to switch the control point first,
1064 // and then correct the offset value in notification process
1065 if (notifyScrollOverCallback_) {
1066 notifyScrollOverCallback_(velocity);
1067 }
1068 // then use corrected offset to make scroll motion.
1069 if (scrollOverCallback_) {
1070 scrollOverCallback_(velocity);
1071 }
1072 }
1073
HandleOverScroll(double velocity)1074 bool Scrollable::HandleOverScroll(double velocity)
1075 {
1076 if (!overScrollCallback_) {
1077 if (edgeEffect_ == EdgeEffect::SPRING) {
1078 ProcessScrollOverCallback(velocity);
1079 return true;
1080 }
1081 if (scrollEndCallback_) {
1082 scrollEndCallback_();
1083 }
1084 return false;
1085 }
1086 // call NestableScrollContainer::HandleOverScroll
1087 return overScrollCallback_(velocity);
1088 }
1089
SetSlipFactor(double SlipFactor)1090 void Scrollable::SetSlipFactor(double SlipFactor)
1091 {
1092 slipFactor_ = std::clamp(SlipFactor, -ADJUSTABLE_VELOCITY, ADJUSTABLE_VELOCITY);
1093 }
1094
UpdateScrollSnapEndWithOffset(double offset)1095 void Scrollable::UpdateScrollSnapEndWithOffset(double offset)
1096 {
1097 if (!isSnapScrollAnimationStop_) {
1098 AnimationOption option;
1099 option.SetDuration(CUSTOM_SPRING_ANIMATION_DURATION);
1100 auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(DEFAULT_SPRING_RESPONSE, DEFAULT_SPRING_DAMP, 0.0f);
1101 option.SetCurve(curve);
1102 if (!snapOffsetProperty_) {
1103 GetSnapProperty();
1104 }
1105 updateSnapAnimationCount_++;
1106 endPos_ -= offset;
1107 snapOffsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
1108 AnimationUtils::StartAnimation(
1109 option,
1110 [weak = AceType::WeakClaim(this)]() {
1111 auto scroll = weak.Upgrade();
1112 CHECK_NULL_VOID(scroll);
1113 scroll->snapOffsetProperty_->Set(scroll->endPos_);
1114 scroll->isSnapScrollAnimationStop_ = false;
1115 },
1116 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1117 ContainerScope scope(id);
1118 auto scroll = weak.Upgrade();
1119 CHECK_NULL_VOID(scroll);
1120 scroll->updateSnapAnimationCount_--;
1121 // avoid current animation being interrupted by the prev animation's finish callback
1122 if (scroll->updateSnapAnimationCount_ == 0) {
1123 scroll->isSnapScrollAnimationStop_ = true;
1124 scroll->ProcessScrollSnapStop();
1125 }
1126 });
1127 isSnapScrollAnimationStop_ = false;
1128 }
1129 }
1130
GetFrictionProperty()1131 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetFrictionProperty()
1132 {
1133 auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1134 auto scroll = weak.Upgrade();
1135 CHECK_NULL_VOID(scroll);
1136 if (scroll->isFrictionAnimationStop_ || scroll->isTouching_) {
1137 return;
1138 }
1139 scroll->isSnapAnimation_ = false;
1140 scroll->ProcessScrollMotion(position);
1141 if (NearEqual(scroll->finalPosition_, position, 1.0)) {
1142 scroll->StopFrictionAnimation();
1143 }
1144 auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
1145 CHECK_NULL_VOID(context);
1146 uint64_t currentVsync = context->GetVsyncTime();
1147 uint64_t diff = currentVsync - scroll->lastVsyncTime_;
1148 if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1149 scroll->frictionVelocity_ = (position - scroll->lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1150 if (NearZero(scroll->frictionVelocity_, FRICTION_VELOCITY_THRESHOLD)) {
1151 scroll->StopFrictionAnimation();
1152 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
1153 }
1154 }
1155 scroll->lastVsyncTime_ = currentVsync;
1156 scroll->lastPosition_ = position;
1157 };
1158 frictionOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1159 return frictionOffsetProperty_;
1160 }
1161
GetSpringProperty()1162 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetSpringProperty()
1163 {
1164 auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1165 auto scroll = weak.Upgrade();
1166 CHECK_NULL_VOID(scroll);
1167 if (scroll->isSpringAnimationStop_) {
1168 return;
1169 }
1170 if (!NearEqual(scroll->finalPosition_, position, SPRING_ACCURACY)) {
1171 scroll->ProcessSpringMotion(position);
1172 return;
1173 }
1174 /*
1175 * In order to prevent accumulation errors, the current position is re obtained to ensure that
1176 * the last frame can accurately stop at the top and bottom positions.
1177 */
1178 if (scroll->currentPositionCallback_) {
1179 double currPos = scroll->currentPositionCallback_();
1180 if (NearEqual(currPos, scroll->currentPos_, 0.5)) {
1181 scroll->currentPos_ = currPos;
1182 }
1183 }
1184 scroll->ProcessSpringMotion(scroll->finalPosition_);
1185 scroll->StopSpringAnimation();
1186 };
1187 springOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1188 return springOffsetProperty_;
1189 }
1190
GetSnapProperty()1191 RefPtr<NodeAnimatablePropertyFloat> Scrollable::GetSnapProperty()
1192 {
1193 auto propertyCallback = [weak = AceType::WeakClaim(this)](float position) {
1194 auto scroll = weak.Upgrade();
1195 CHECK_NULL_VOID(scroll);
1196 if (scroll->isSnapScrollAnimationStop_ && scroll->isSnapAnimationStop_) {
1197 return;
1198 }
1199 auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
1200 CHECK_NULL_VOID(context);
1201 uint64_t currentVsync = context->GetVsyncTime();
1202 uint64_t diff = currentVsync - scroll->lastVsyncTime_;
1203 if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1204 scroll->snapVelocity_ = (position - scroll->currentPos_) / diff * MILLOS_PER_NANO_SECONDS;
1205 }
1206 scroll->lastVsyncTime_ = currentVsync;
1207 if (NearEqual(scroll->endPos_, position, SPRING_ACCURACY)) {
1208 if (!scroll->isSnapScrollAnimationStop_) {
1209 scroll->ProcessScrollSnapMotion(scroll->endPos_);
1210 } else if (!scroll->isSnapAnimationStop_) {
1211 scroll->isSnapAnimation_ = true;
1212 scroll->ProcessScrollMotion(scroll->endPos_);
1213 }
1214 scroll->StopSnapAnimation();
1215 } else {
1216 if (!scroll->isSnapScrollAnimationStop_) {
1217 scroll->ProcessScrollSnapMotion(position);
1218 } else if (!scroll->isSnapAnimationStop_) {
1219 scroll->isSnapAnimation_ = true;
1220 scroll->ProcessScrollMotion(position);
1221 }
1222 }
1223 };
1224 snapOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1225 return snapOffsetProperty_;
1226 }
1227
StopFrictionAnimation()1228 void Scrollable::StopFrictionAnimation()
1229 {
1230 if (!isFrictionAnimationStop_) {
1231 ACE_SCOPED_TRACE("StopFrictionAnimation, id:%d, tag:%s", nodeId_, nodeTag_.c_str());
1232 isFrictionAnimationStop_ = true;
1233 AnimationOption option;
1234 option.SetCurve(Curves::EASE);
1235 option.SetDuration(0);
1236 AnimationUtils::StartAnimation(
1237 option,
1238 [weak = AceType::WeakClaim(this)]() {
1239 auto scroll = weak.Upgrade();
1240 CHECK_NULL_VOID(scroll);
1241 scroll->frictionOffsetProperty_->Set(scroll->currentPos_);
1242 },
1243 nullptr);
1244 }
1245 }
1246
StopSpringAnimation(bool reachFinalPosition)1247 void Scrollable::StopSpringAnimation(bool reachFinalPosition)
1248 {
1249 if (!isSpringAnimationStop_) {
1250 ACE_SCOPED_TRACE(
1251 "StopSpringAnimation, reachFinalPosition:%u, id:%d, tag:%s", reachFinalPosition, nodeId_, nodeTag_.c_str());
1252 isSpringAnimationStop_ = true;
1253 isFadingAway_ = false;
1254 AnimationOption option;
1255 option.SetCurve(Curves::EASE);
1256 option.SetDuration(0);
1257 AnimationUtils::StartAnimation(
1258 option,
1259 [weak = AceType::WeakClaim(this), reachFinalPosition]() {
1260 auto scroll = weak.Upgrade();
1261 CHECK_NULL_VOID(scroll);
1262 if (reachFinalPosition) {
1263 // ensure that the spring animation is restored to its final position.
1264 scroll->ProcessSpringMotion(scroll->finalPosition_);
1265 // use non final position to stop animation, otherwise the animation cannot be stoped.
1266 scroll->springOffsetProperty_->Set(scroll->finalPosition_ - 1.f);
1267 } else {
1268 // avoid top edge spring can not stop
1269 scroll->springOffsetProperty_->Set(scroll->currentPos_);
1270 }
1271 },
1272 nullptr);
1273 }
1274 currentVelocity_ = 0.0;
1275 }
1276
StopSnapAnimation()1277 void Scrollable::StopSnapAnimation()
1278 {
1279 if (!isSnapAnimationStop_ || !isSnapScrollAnimationStop_) {
1280 ACE_SCOPED_TRACE("StopSnapAnimation, isSnapAnimationStop_:%u, isSnapScrollAnimationStop_:%u, id:%d, tag:%s",
1281 isSnapAnimationStop_, isSnapScrollAnimationStop_, nodeId_, nodeTag_.c_str());
1282 isSnapAnimationStop_ = true;
1283 isSnapScrollAnimationStop_ = true;
1284 AnimationOption option;
1285 option.SetCurve(Curves::EASE);
1286 option.SetDuration(0);
1287 AnimationUtils::StartAnimation(
1288 option,
1289 [weak = AceType::WeakClaim(this)]() {
1290 auto scroll = weak.Upgrade();
1291 CHECK_NULL_VOID(scroll);
1292 scroll->snapOffsetProperty_->Set(scroll->currentPos_);
1293 },
1294 nullptr);
1295 }
1296 }
1297
IsMouseWheelScroll(const GestureEvent & info)1298 inline bool Scrollable::IsMouseWheelScroll(const GestureEvent& info)
1299 {
1300 return info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() != SourceTool::TOUCHPAD;
1301 }
1302
OnCollectTouchTarget(TouchTestResult & result,const RefPtr<FrameNode> & frameNode,const RefPtr<TargetComponent> & targetComponent,ResponseLinkResult & responseLinkResult)1303 void Scrollable::OnCollectTouchTarget(TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
1304 const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult)
1305 {
1306 if (panRecognizerNG_) {
1307 panRecognizerNG_->SetNodeId(frameNode->GetId());
1308 panRecognizerNG_->AttachFrameNode(frameNode);
1309 panRecognizerNG_->SetTargetComponent(targetComponent);
1310 panRecognizerNG_->SetIsSystemGesture(true);
1311 panRecognizerNG_->SetRecognizerType(GestureTypeName::PAN_GESTURE);
1312 result.emplace_back(panRecognizerNG_);
1313 responseLinkResult.emplace_back(panRecognizerNG_);
1314 }
1315 }
1316 } // namespace OHOS::Ace::NG
1317