• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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/scroll/inner/scroll_bar.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/animation/curve_animation.h"
20 #include "core/animation/curves.h"
21 #include "core/components/scroll/scroll_bar_theme.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 
24 namespace OHOS::Ace::NG {
25 namespace {
26 constexpr int32_t STOP_DURATION = 2000; // 2000ms
27 constexpr float KEY_TIME_START = 0.0f;
28 constexpr float KEY_TIME_MIDDLE = 0.7f;
29 constexpr float KEY_TIME_END = 1.0f;
30 constexpr int32_t BAR_EXPAND_DURATION = 150; // 150ms, scroll bar width expands from 4dp to 8dp
31 constexpr int32_t BAR_SHRINK_DURATION = 250; // 250ms, scroll bar width shrinks from 8dp to 4dp
32 constexpr int32_t BAR_ADAPT_DURATION = 400;  // 400ms, scroll bar adapts to the size changes of components
33 constexpr double BAR_ADAPT_EPSLION = 1.0;
34 } // namespace
35 
ScrollBar()36 ScrollBar::ScrollBar()
37 {
38     InitTheme();
39 }
40 
ScrollBar(DisplayMode displayMode,ShapeMode shapeMode,PositionMode positionMode)41 ScrollBar::ScrollBar(DisplayMode displayMode, ShapeMode shapeMode, PositionMode positionMode)
42     : displayMode_(displayMode), shapeMode_(shapeMode), positionMode_(positionMode)
43 {
44     InitTheme();
45 }
46 
InitTheme()47 void ScrollBar::InitTheme()
48 {
49     auto pipelineContext = PipelineContext::GetCurrentContext();
50     CHECK_NULL_VOID(pipelineContext);
51     auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
52     CHECK_NULL_VOID(theme);
53     SetInactiveWidth(theme->GetNormalWidth());
54     SetNormalWidth(theme->GetNormalWidth());
55     SetActiveWidth(theme->GetActiveWidth());
56     SetTouchWidth(theme->GetTouchWidth());
57     SetReservedHeight(theme->GetReservedHeight());
58     SetMinHeight(theme->GetMinHeight());
59     SetMinDynamicHeight(theme->GetMinDynamicHeight());
60     SetBackgroundColor(theme->GetBackgroundColor());
61     SetForegroundColor(theme->GetForegroundColor());
62     SetPadding(theme->GetPadding());
63     SetScrollable(true);
64 }
65 
InBarTouchRegion(const Point & point) const66 bool ScrollBar::InBarTouchRegion(const Point& point) const
67 {
68     if (NeedScrollBar() && shapeMode_ == ShapeMode::RECT) {
69         return touchRegion_.IsInRegion(point);
70     }
71     return false;
72 }
73 
InBarActiveRegion(const Point & point) const74 bool ScrollBar::InBarActiveRegion(const Point& point) const
75 {
76     if (NeedScrollBar() && shapeMode_ == ShapeMode::RECT) {
77         return activeRect_.IsInRegion(point);
78     }
79     return false;
80 }
81 
FlushBarWidth()82 void ScrollBar::FlushBarWidth()
83 {
84     SetBarRegion(paintOffset_, viewPortSize_);
85     if (shapeMode_ == ShapeMode::RECT) {
86         SetRectTrickRegion(paintOffset_, viewPortSize_, lastOffset_, estimatedHeight_);
87     } else {
88         SetRoundTrickRegion(paintOffset_, viewPortSize_, lastOffset_, estimatedHeight_);
89     }
90 }
91 
UpdateScrollBarRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)92 void ScrollBar::UpdateScrollBarRegion(
93     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
94 {
95     // return if nothing changes to avoid changing opacity
96     if (!positionModeUpdate_ && paintOffset_ == offset && viewPortSize_ == size && lastOffset_ == lastOffset &&
97         NearEqual(estimatedHeight_, estimatedHeight, 0.000001f)) {
98         return;
99     }
100     if (!NearZero(estimatedHeight)) {
101         paintOffset_ = offset;
102         viewPortSize_ = size;
103         lastOffset_ = lastOffset;
104         estimatedHeight_ = estimatedHeight;
105         opacity_ = UINT8_MAX;
106         SetBarRegion(offset, size);
107         if (shapeMode_ == ShapeMode::RECT) {
108             SetRectTrickRegion(offset, size, lastOffset, estimatedHeight);
109         } else {
110             SetRoundTrickRegion(offset, size, lastOffset, estimatedHeight);
111         }
112         positionModeUpdate_ = false;
113     }
114     OnScrollEnd();
115 }
116 
UpdateActiveRectSize(double activeSize)117 void ScrollBar::UpdateActiveRectSize(double activeSize)
118 {
119     if (positionMode_ == PositionMode::LEFT || positionMode_ == PositionMode::RIGHT) {
120         activeRect_.SetHeight(activeSize);
121         touchRegion_.SetHeight(activeSize);
122     } else if (positionMode_ == PositionMode::BOTTOM) {
123         activeRect_.SetWidth(activeSize);
124         touchRegion_.SetWidth(activeSize);
125     }
126 }
127 
UpdateActiveRectOffset(double activeMainOffset)128 void ScrollBar::UpdateActiveRectOffset(double activeMainOffset)
129 {
130     if (positionMode_ == PositionMode::LEFT || positionMode_ == PositionMode::RIGHT) {
131         activeMainOffset = std::min(activeMainOffset, barRegionSize_ - activeRect_.Height());
132         activeRect_.SetTop(activeMainOffset);
133         touchRegion_.SetTop(activeMainOffset);
134     } else if (positionMode_ == PositionMode::BOTTOM) {
135         activeMainOffset = std::min(activeMainOffset, barRegionSize_ - activeRect_.Width());
136         activeRect_.SetLeft(activeMainOffset);
137         touchRegion_.SetLeft(activeMainOffset);
138     }
139 }
140 
SetBarRegion(const Offset & offset,const Size & size)141 void ScrollBar::SetBarRegion(const Offset& offset, const Size& size)
142 {
143     double normalWidth = NormalizeToPx(normalWidth_);
144     if (shapeMode_ == ShapeMode::RECT) {
145         double height = std::max(size.Height() - NormalizeToPx(reservedHeight_), 0.0);
146         if (positionMode_ == PositionMode::LEFT) {
147             barRect_ = Rect(0.0, 0.0, normalWidth, height) + offset;
148         } else if (positionMode_ == PositionMode::RIGHT) {
149             barRect_ =
150                 Rect(size.Width() - normalWidth - NormalizeToPx(padding_.Right()), 0.0, normalWidth, height) + offset;
151         } else if (positionMode_ == PositionMode::BOTTOM) {
152             auto scrollBarWidth = std::max(size.Width() - NormalizeToPx(reservedHeight_), 0.0);
153             barRect_ =
154                 Rect(0.0, size.Height() - normalWidth - NormalizeToPx(padding_.Bottom()), scrollBarWidth, normalWidth) +
155                 offset;
156         }
157     }
158 }
159 
SetRectTrickRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)160 void ScrollBar::SetRectTrickRegion(
161     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
162 {
163     double mainSize = (positionMode_ == PositionMode::BOTTOM ? size.Width() : size.Height());
164     barRegionSize_ = std::max(mainSize - NormalizeToPx(reservedHeight_), 0.0);
165     if (LessOrEqual(estimatedHeight, 0.0)) {
166         return;
167     }
168     double activeSize = barRegionSize_ * mainSize / estimatedHeight - outBoundary_;
169     if (!NearEqual(mainSize, estimatedHeight)) {
170         if (!NearZero(outBoundary_)) {
171             activeSize = std::max(
172                 std::max(activeSize, NormalizeToPx(minHeight_) - outBoundary_), NormalizeToPx(minDynamicHeight_));
173         } else {
174             activeSize = std::max(activeSize, NormalizeToPx(minHeight_));
175         }
176         double normalWidth = NormalizeToPx(normalWidth_);
177         if (LessOrEqual(activeSize, normalWidth)) {
178             activeSize = normalWidth;
179         }
180         double lastMainOffset =
181             std::max(positionMode_ == PositionMode::BOTTOM ? lastOffset.GetX() : lastOffset.GetY(), 0.0);
182         offsetScale_ = (barRegionSize_ - activeSize) / (estimatedHeight - mainSize);
183         double activeMainOffset = offsetScale_ * lastMainOffset;
184         bool canUseAnimation = !inSpring && !positionModeUpdate_;
185         activeMainOffset = std::min(activeMainOffset, barRegionSize_ - activeSize);
186         double inactiveSize = 0.0;
187         double inactiveMainOffset = 0.0;
188         scrollableOffset_ = activeMainOffset;
189         if (positionMode_ == PositionMode::LEFT) {
190             inactiveSize = activeRect_.Height();
191             inactiveMainOffset = activeRect_.Top();
192             if (adaptAnimator_ && adaptAnimator_->IsRunning()) {
193                 activeRect_ =
194                     Rect(-NormalizeToPx(position_), activeRect_.Top(), normalWidth, activeRect_.Height()) + offset;
195             } else {
196                 activeRect_ = Rect(-NormalizeToPx(position_), activeMainOffset, normalWidth, activeSize) + offset;
197             }
198             touchRegion_ = activeRect_ + Size(NormalizeToPx(touchWidth_), 0);
199         } else if (positionMode_ == PositionMode::RIGHT) {
200             inactiveSize = activeRect_.Height();
201             inactiveMainOffset = activeRect_.Top();
202             double x = size.Width() - normalWidth - NormalizeToPx(padding_.Right()) + NormalizeToPx(position_);
203             if (adaptAnimator_ && adaptAnimator_->IsRunning()) {
204                 activeRect_ = Rect(x, activeRect_.Top(), normalWidth, activeRect_.Height()) + offset;
205             } else {
206                 activeRect_ = Rect(x, activeMainOffset, normalWidth, activeSize) + offset;
207             }
208             // Update the hot region
209             touchRegion_ =
210                 activeRect_ -
211                 Offset(
212                     NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Right()), 0.0) +
213                 Size(NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_), 0);
214         } else if (positionMode_ == PositionMode::BOTTOM) {
215             inactiveSize = activeRect_.Width();
216             inactiveMainOffset = activeRect_.Left();
217             auto positionY = size.Height() - normalWidth - NormalizeToPx(padding_.Bottom()) + NormalizeToPx(position_);
218             if (adaptAnimator_ && adaptAnimator_->IsRunning()) {
219                 activeRect_ = Rect(activeRect_.Left(), positionY, activeRect_.Width(), normalWidth) + offset;
220             } else {
221                 activeRect_ = Rect(activeMainOffset, positionY, activeSize, normalWidth) + offset;
222             }
223             auto hotRegionOffset = Offset(
224                 0.0, NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Bottom()));
225             auto hotRegionSize = Size(0, NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_));
226             touchRegion_ = activeRect_ - hotRegionOffset + hotRegionSize;
227         }
228         // If the scrollBar length changes, start the adaptation animation
229         if (!NearZero(inactiveSize) && !NearEqual(activeSize, inactiveSize, BAR_ADAPT_EPSLION) && canUseAnimation) {
230             PlayAdaptAnimation(activeSize, activeMainOffset, inactiveSize, inactiveMainOffset);
231         }
232     }
233 }
234 
SetRoundTrickRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)235 void ScrollBar::SetRoundTrickRegion(
236     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
237 {
238     double diameter = std::min(size.Width(), size.Height());
239     if (!NearEqual(estimatedHeight, diameter)) {
240         double maxAngle = bottomAngle_ - topAngle_;
241         trickSweepAngle_ = std::max(diameter * maxAngle / estimatedHeight, minAngle_);
242         double lastOffsetY = std::max(lastOffset.GetY(), 0.0);
243         double trickStartAngle = (maxAngle - trickSweepAngle_) * lastOffsetY / (estimatedHeight - diameter);
244         trickStartAngle = std::clamp(0.0, trickStartAngle, maxAngle) - maxAngle * FACTOR_HALF;
245         if (positionMode_ == PositionMode::LEFT) {
246             if (trickStartAngle > 0.0) {
247                 trickStartAngle_ = STRAIGHT_ANGLE - trickStartAngle;
248             } else {
249                 trickStartAngle_ = -(trickStartAngle + STRAIGHT_ANGLE);
250             }
251             trickSweepAngle_ = -trickSweepAngle_;
252         } else {
253             trickStartAngle_ = trickStartAngle;
254         }
255     }
256 }
257 
NeedScrollBar() const258 bool ScrollBar::NeedScrollBar() const
259 {
260     return displayMode_ == DisplayMode::AUTO || displayMode_ == DisplayMode::ON;
261 }
262 
NeedPaint() const263 bool ScrollBar::NeedPaint() const
264 {
265     return NeedScrollBar() && isScrollable_ && GreatNotEqual(normalWidth_.Value(), 0.0) && opacity_ > 0;
266 }
267 
GetNormalWidthToPx() const268 double ScrollBar::GetNormalWidthToPx() const
269 {
270     return NormalizeToPx(normalWidth_);
271 }
272 
CalcPatternOffset(float scrollBarOffset) const273 float ScrollBar::CalcPatternOffset(float scrollBarOffset) const
274 {
275     if (!isDriving_ || NearZero(offsetScale_)) {
276         return scrollBarOffset;
277     }
278     return -scrollBarOffset / offsetScale_;
279 }
280 
NormalizeToPx(const Dimension & dimension) const281 double ScrollBar::NormalizeToPx(const Dimension& dimension) const
282 {
283     auto pipelineContext = PipelineContext::GetCurrentContext();
284     CHECK_NULL_RETURN(pipelineContext, 0.0);
285     return pipelineContext->NormalizeToPx(dimension);
286 }
287 
SetGestureEvent()288 void ScrollBar::SetGestureEvent()
289 {
290     if (!touchEvent_) {
291         touchEvent_ = MakeRefPtr<TouchEventImpl>([weak = WeakClaim(this)](const TouchEventInfo& info) {
292             auto scrollBar = weak.Upgrade();
293             CHECK_NULL_VOID(scrollBar);
294             if (info.GetTouches().empty()) {
295                 return;
296             }
297             auto touch = info.GetTouches().front();
298             if (touch.GetTouchType() == TouchType::DOWN) {
299                 Point point(touch.GetLocalLocation().GetX(), touch.GetLocalLocation().GetY());
300                 bool inTouchRegion = scrollBar->InBarTouchRegion(point);
301                 scrollBar->SetPressed(inTouchRegion);
302                 scrollBar->SetDriving(inTouchRegion);
303                 if (inTouchRegion && !scrollBar->IsHover()) {
304                     scrollBar->PlayGrowAnimation();
305                 }
306                 if (scrollBar->scrollEndAnimator_ && !scrollBar->scrollEndAnimator_->IsStopped()) {
307                     scrollBar->scrollEndAnimator_->Stop();
308                 }
309                 scrollBar->MarkNeedRender();
310             }
311             if (info.GetTouches().front().GetTouchType() == TouchType::UP) {
312                 if (scrollBar->IsPressed() && !scrollBar->IsHover()) {
313                     scrollBar->PlayShrinkAnimation();
314                 }
315                 scrollBar->SetPressed(false);
316                 scrollBar->MarkNeedRender();
317             }
318         });
319     }
320     if (!touchAnimator_) {
321         touchAnimator_ = AceType::MakeRefPtr<Animator>(PipelineContext::GetCurrentContext());
322     }
323 }
324 
SetMouseEvent()325 void ScrollBar::SetMouseEvent()
326 {
327     if (mouseEvent_) {
328         return;
329     }
330     mouseEvent_ = MakeRefPtr<InputEvent>([weak = WeakClaim(this)](MouseInfo& info) {
331         auto scrollBar = weak.Upgrade();
332         CHECK_NULL_VOID_NOLOG(scrollBar);
333         Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
334         bool inRegion = scrollBar->InBarActiveRegion(point);
335         if (inRegion && !scrollBar->IsHover()) {
336             if (!scrollBar->IsPressed()) {
337                 scrollBar->PlayGrowAnimation();
338             }
339             scrollBar->SetHover(true);
340             if (scrollBar->scrollEndAnimator_ && !scrollBar->scrollEndAnimator_->IsStopped()) {
341                 scrollBar->scrollEndAnimator_->Stop();
342             }
343             scrollBar->MarkNeedRender();
344         }
345         if (scrollBar->IsHover() && !inRegion) {
346             if (!scrollBar->IsPressed()) {
347                 scrollBar->PlayShrinkAnimation();
348                 if (scrollBar->GetDisplayMode() == DisplayMode::AUTO) {
349                     scrollBar->PlayBarEndAnimation();
350                 }
351             }
352             scrollBar->SetHover(false);
353             scrollBar->MarkNeedRender();
354         }
355     });
356 }
357 
PlayAdaptAnimation(double activeSize,double activeMainOffset,double inactiveSize,double inactiveMainOffset)358 void ScrollBar::PlayAdaptAnimation(
359     double activeSize, double activeMainOffset, double inactiveSize, double inactiveMainOffset)
360 {
361     if (adaptAnimator_ && adaptAnimator_->IsRunning()) {
362         return;
363     }
364     if (!adaptAnimator_) {
365         adaptAnimator_ = AceType::MakeRefPtr<Animator>(PipelineContext::GetCurrentContext());
366     }
367     adaptAnimator_->ClearInterpolators();
368     // Animate the mainSize of the ScrollBar
369     auto sizeAnimation = AceType::MakeRefPtr<CurveAnimation<double>>(inactiveSize, activeSize, Curves::FRICTION);
370     sizeAnimation->AddListener([weakBar = AceType::WeakClaim(this)](double value) {
371         auto scrollBar = weakBar.Upgrade();
372         if (scrollBar) {
373             scrollBar->UpdateActiveRectSize(value);
374             scrollBar->MarkNeedRender();
375         }
376     });
377     // Animate the mainOffset of the ScrollBar
378     auto offsetAnimation =
379         AceType::MakeRefPtr<CurveAnimation<double>>(inactiveMainOffset, activeMainOffset, Curves::FRICTION);
380     offsetAnimation->AddListener(
381         [weakBar = AceType::WeakClaim(this), inactiveMainOffset, activeMainOffset](double value) {
382             auto scrollBar = weakBar.Upgrade();
383             if (scrollBar) {
384                 auto top = scrollBar->GetPositionMode() == PositionMode::BOTTOM ? scrollBar->activeRect_.Left() :
385                     scrollBar->activeRect_.Top();
386                 if (NearEqual(top, activeMainOffset, 0.000001f) || NearEqual(top, inactiveMainOffset, 0.000001f)) {
387                     scrollBar->UpdateActiveRectOffset(value);
388                 } else {
389                     scrollBar->UpdateActiveRectOffset(value + scrollBar->scrollableOffset_ - activeMainOffset);
390                 }
391                 scrollBar->MarkNeedRender();
392             }
393         });
394     adaptAnimator_->AddInterpolator(sizeAnimation);
395     adaptAnimator_->AddInterpolator(offsetAnimation);
396     adaptAnimator_->SetDuration(BAR_ADAPT_DURATION);
397 
398     UpdateActiveRectSize(inactiveSize);
399     UpdateActiveRectOffset(inactiveMainOffset);
400 
401     adaptAnimator_->Play();
402 }
403 
PlayGrowAnimation()404 void ScrollBar::PlayGrowAnimation()
405 {
406     if (!touchAnimator_->IsStopped()) {
407         touchAnimator_->Stop();
408     }
409     touchAnimator_->ClearInterpolators();
410     auto activeWidth = activeWidth_.ConvertToPx();
411     auto inactiveWidth = inactiveWidth_.ConvertToPx();
412 
413     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(inactiveWidth, activeWidth, Curves::SHARP);
414     animation->AddListener([weakBar = AceType::WeakClaim(this)](double value) {
415         auto scrollBar = weakBar.Upgrade();
416         if (scrollBar) {
417             scrollBar->normalWidth_ = Dimension(value, DimensionUnit::PX);
418             scrollBar->FlushBarWidth();
419             scrollBar->MarkNeedRender();
420         }
421     });
422     touchAnimator_->AddInterpolator(animation);
423     touchAnimator_->SetDuration(BAR_EXPAND_DURATION);
424     touchAnimator_->Play();
425 }
426 
PlayShrinkAnimation()427 void ScrollBar::PlayShrinkAnimation()
428 {
429     if (!touchAnimator_->IsStopped()) {
430         touchAnimator_->Stop();
431     }
432     touchAnimator_->ClearInterpolators();
433     auto activeWidth = activeWidth_.ConvertToPx();
434     auto inactiveWidth = inactiveWidth_.ConvertToPx();
435 
436     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(activeWidth, inactiveWidth, Curves::SHARP);
437     animation->AddListener([weakBar = AceType::WeakClaim(this)](double value) {
438         auto scrollBar = weakBar.Upgrade();
439         if (scrollBar) {
440             scrollBar->normalWidth_ = Dimension(value, DimensionUnit::PX);
441             scrollBar->FlushBarWidth();
442             scrollBar->MarkNeedRender();
443         }
444     });
445     touchAnimator_->AddInterpolator(animation);
446     touchAnimator_->SetDuration(BAR_SHRINK_DURATION);
447     touchAnimator_->Play();
448 }
449 
PlayBarEndAnimation()450 void ScrollBar::PlayBarEndAnimation()
451 {
452     if (scrollEndAnimator_ && !scrollEndAnimator_->IsStopped()) {
453         scrollEndAnimator_->Stop();
454     }
455     if (scrollEndAnimator_) {
456         scrollEndAnimator_->Play();
457         return;
458     }
459 
460     scrollEndAnimator_ = AceType::MakeRefPtr<Animator>(PipelineContext::GetCurrentContext());
461     auto hiddenStartKeyframe = AceType::MakeRefPtr<Keyframe<int32_t>>(KEY_TIME_START, UINT8_MAX);
462     auto hiddenMiddleKeyframe = AceType::MakeRefPtr<Keyframe<int32_t>>(KEY_TIME_MIDDLE, UINT8_MAX);
463     auto hiddenEndKeyframe = AceType::MakeRefPtr<Keyframe<int32_t>>(KEY_TIME_END, 0);
464     hiddenMiddleKeyframe->SetCurve(Curves::LINEAR);
465     hiddenEndKeyframe->SetCurve(Curves::FRICTION);
466 
467     auto animation = AceType::MakeRefPtr<KeyframeAnimation<int32_t>>();
468     animation->AddKeyframe(hiddenStartKeyframe);
469     animation->AddKeyframe(hiddenMiddleKeyframe);
470     animation->AddKeyframe(hiddenEndKeyframe);
471     animation->AddListener([weakBar = AceType::WeakClaim(this)](int32_t value) {
472         auto scrollBar = weakBar.Upgrade();
473         if (scrollBar) {
474             scrollBar->opacity_ = value;
475             scrollBar->MarkNeedRender();
476         }
477     });
478     scrollEndAnimator_->AddInterpolator(animation);
479     scrollEndAnimator_->SetDuration(STOP_DURATION);
480     scrollEndAnimator_->Play();
481 }
482 } // namespace OHOS::Ace::NG