• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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_overlay_modifier.h"
17 
18 #include "core/components_ng/render/drawing_prop_convertor.h"
19 
20 namespace OHOS::Ace::NG {
21 namespace {
22 constexpr double FULL_ALPHA = 255.0;
23 constexpr float HALF = 0.5f;
24 constexpr float SPRING_MOTION_RESPONSE = 0.314f;
25 constexpr float SPRING_MOTION_DAMPING_FRACTION = 0.95f;
26 constexpr int32_t BAR_DISAPPEAR_DURATION = 300;  // 300ms
27 constexpr int32_t BAR_APPEAR_DURATION = 100;     // 100ms
28 constexpr int32_t BAR_GROW_DURATION = 150;       // 150ms, scroll bar width expands from 4dp to 8dp
29 constexpr int32_t BAR_SHRINK_DURATION = 250;     // 250ms, scroll bar width shrinks from 8dp to 4dp
30 constexpr int32_t BAR_DISAPPEAR_FRAME_RATE = 15; // 15fps, the expected frame rate of opacity animation
31 constexpr int32_t BAR_DISAPPEAR_MIN_FRAME_RATE = 0;
32 constexpr int32_t BAR_DISAPPEAR_MAX_FRAME_RATE = 90;
33 constexpr float ADAPT_ACCURACY = 0.5f;
34 } // namespace
35 
ScrollBarOverlayModifier(const OffsetF & barOffset,const SizeF & barSize)36 ScrollBarOverlayModifier::ScrollBarOverlayModifier(const OffsetF& barOffset, const SizeF& barSize)
37 {
38     barX_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(barOffset.GetX());
39     AttachProperty(barX_);
40     barY_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(barOffset.GetY());
41     AttachProperty(barY_);
42     barWidth_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(barSize.Width());
43     AttachProperty(barWidth_);
44     barHeight_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(barSize.Height());
45     AttachProperty(barHeight_);
46     opacity_ = AceType::MakeRefPtr<AnimatablePropertyUint8>(UINT8_MAX);
47     AttachProperty(opacity_);
48     barColor_ = AceType::MakeRefPtr<PropertyColor>(Color());
49     AttachProperty(barColor_);
50 }
51 
onDraw(DrawingContext & drawingContext)52 void ScrollBarOverlayModifier::onDraw(DrawingContext& drawingContext)
53 {
54     CHECK_NULL_VOID(opacity_);
55     CHECK_NULL_VOID(barColor_);
56     CHECK_NULL_VOID(barWidth_);
57     CHECK_NULL_VOID(barHeight_);
58     CHECK_NULL_VOID(barX_);
59     CHECK_NULL_VOID(barY_);
60     CheckMainModeNearEqual();
61     auto barWidth = barWidth_->Get();
62     auto barHeight = barHeight_->Get();
63     auto barX = barX_->Get();
64     auto barY = barY_->Get();
65     if (!NearZero(barWidth) && !NearZero(barHeight)) {
66         auto& canvas = drawingContext.canvas;
67         RSBrush brush;
68         brush.SetBlendMode(RSBlendMode::SRC_OVER);
69         brush.SetAntiAlias(true);
70         RSRect fgRect(barX, barY, barX + barWidth, barY + barHeight);
71         double filletRadius = barWidth * HALF;
72         RSColor barColor = ToRSColor(barColor_->Get().BlendOpacity(opacity_->Get() / FULL_ALPHA));
73         brush.SetColor(barColor);
74         canvas.AttachBrush(brush);
75         canvas.DrawRoundRect({ fgRect, filletRadius, filletRadius });
76         canvas.DetachBrush();
77     }
78 }
79 
SetOffset(const OffsetF & barOffset)80 void ScrollBarOverlayModifier::SetOffset(const OffsetF& barOffset)
81 {
82     CHECK_NULL_VOID(barX_);
83     CHECK_NULL_VOID(barY_);
84     barX_->Set(barOffset.GetX());
85     barY_->Set(barOffset.GetY());
86 }
87 
SetSize(const SizeF & barSize)88 void ScrollBarOverlayModifier::SetSize(const SizeF& barSize)
89 {
90     CHECK_NULL_VOID(barWidth_);
91     CHECK_NULL_VOID(barHeight_);
92     barWidth_->Set(barSize.Width());
93     barHeight_->Set(barSize.Height());
94 }
95 
SetRect(const Rect & fgRect)96 void ScrollBarOverlayModifier::SetRect(const Rect& fgRect)
97 {
98     SetOffset(OffsetF(fgRect.Left(), fgRect.Top()));
99     SetSize(SizeF(fgRect.Width(), fgRect.Height()));
100 }
101 
SetMainModeSize(const Size & size)102 void ScrollBarOverlayModifier::SetMainModeSize(const Size& size)
103 {
104     if (positionMode_ == PositionMode::BOTTOM) {
105         CHECK_NULL_VOID(barWidth_);
106         barWidth_->Set(size.Width());
107     } else {
108         CHECK_NULL_VOID(barHeight_);
109         barHeight_->Set(size.Height());
110     }
111 }
112 
SetCrossModeSize(const Size & size)113 void ScrollBarOverlayModifier::SetCrossModeSize(const Size& size)
114 {
115     if (positionMode_ == PositionMode::BOTTOM) {
116         CHECK_NULL_VOID(barHeight_);
117         barHeight_->Set(size.Height());
118     } else {
119         CHECK_NULL_VOID(barWidth_);
120         barWidth_->Set(size.Width());
121     }
122 }
123 
SetMainModeOffset(const Offset & offset)124 void ScrollBarOverlayModifier::SetMainModeOffset(const Offset& offset)
125 {
126     if (positionMode_ == PositionMode::BOTTOM) {
127         CHECK_NULL_VOID(barX_);
128         barX_->Set(offset.GetX());
129     } else {
130         CHECK_NULL_VOID(barY_);
131         barY_->Set(offset.GetY());
132     }
133 }
134 
SetCrossModeOffset(const Offset & offset)135 void ScrollBarOverlayModifier::SetCrossModeOffset(const Offset& offset)
136 {
137     if (positionMode_ == PositionMode::BOTTOM) {
138         CHECK_NULL_VOID(barY_);
139         barY_->Set(offset.GetY());
140     } else {
141         CHECK_NULL_VOID(barX_);
142         barX_->Set(offset.GetX());
143     }
144 }
145 
StartBarAnimation(HoverAnimationType hoverAnimationType,OpacityAnimationType opacityAnimationType,bool needAdaptAnimation,const Rect & fgRect)146 void ScrollBarOverlayModifier::StartBarAnimation(HoverAnimationType hoverAnimationType,
147     OpacityAnimationType opacityAnimationType, bool needAdaptAnimation, const Rect& fgRect)
148 {
149     CHECK_NULL_VOID(barX_);
150     CHECK_NULL_VOID(barY_);
151     CHECK_NULL_VOID(barWidth_);
152     CHECK_NULL_VOID(barHeight_);
153     if (opacityAnimationType == OpacityAnimationType::NONE && GetOpacity() == 0) {
154         AnimationUtils::ExecuteWithoutAnimation([weak = AceType::WeakClaim(this), fgRect]() {
155             auto modifier = weak.Upgrade();
156             CHECK_NULL_VOID(modifier);
157             modifier->SetRect(fgRect);
158         });
159     } else if (hoverAnimationType == HoverAnimationType::NONE && !needAdaptAnimation) {
160         SetRect(fgRect);
161     } else {
162         StartHoverAnimation(fgRect, hoverAnimationType);
163         StartAdaptAnimation(fgRect, needAdaptAnimation);
164     }
165     if (opacityAnimationType != OpacityAnimationType::NONE && isScrollable_) {
166         StartOpacityAnimation(opacityAnimationType);
167     }
168 }
169 
StartAdaptAnimation(const Rect & fgRect,bool needAdaptAnimation)170 void ScrollBarOverlayModifier::StartAdaptAnimation(const Rect& fgRect, bool needAdaptAnimation)
171 {
172     CHECK_NULL_VOID(needAdaptAnimation);
173     AnimationOption option;
174     auto motion = AceType::MakeRefPtr<ResponsiveSpringMotion>(SPRING_MOTION_RESPONSE, SPRING_MOTION_DAMPING_FRACTION);
175     option.SetCurve(motion);
176     isAdaptAnimationStop_ = false;
177     adaptAnimation_ = AnimationUtils::StartAnimation(option, [weak = WeakClaim(this), fgRect]() {
178         auto modifier = weak.Upgrade();
179         CHECK_NULL_VOID(modifier);
180         modifier->SetMainModeSize(fgRect.GetSize());
181         modifier->SetMainModeOffset(fgRect.GetOffset());
182     });
183 }
184 
StopAdaptAnimation()185 void ScrollBarOverlayModifier::StopAdaptAnimation()
186 {
187     if (adaptAnimation_) {
188         isAdaptAnimationStop_ = true;
189         AnimationUtils::StopAnimation(adaptAnimation_);
190     }
191 }
192 
StartHoverAnimation(const Rect & fgRect,HoverAnimationType hoverAnimationType)193 void ScrollBarOverlayModifier::StartHoverAnimation(const Rect& fgRect, HoverAnimationType hoverAnimationType)
194 {
195     // Only the cross axis offset is updated, the main axis offset will be updated by adaptAnimation.
196     if (hoverAnimationType == HoverAnimationType::NONE) {
197         SetCrossModeSize(fgRect.GetSize());
198         SetCrossModeOffset(fgRect.GetOffset());
199         return;
200     }
201     if (hoverAnimationType != hoverAnimatingType_) {
202         StopHoverAnimation();
203     }
204     // In order to only play the offset of the hover part in the animation, update the cross axis offset in advance
205     SetCrossModeOffset(fgRect.GetOffset() + GetHoverOffset(fgRect.GetSize()));
206     hoverAnimatingType_ = hoverAnimationType;
207     AnimationOption option;
208     option.SetCurve(Curves::SHARP);
209     if (hoverAnimatingType_ == HoverAnimationType::GROW) {
210         option.SetDuration(BAR_GROW_DURATION);
211     } else if (hoverAnimatingType_ == HoverAnimationType::SHRINK) {
212         option.SetDuration(BAR_SHRINK_DURATION);
213     }
214     hoverAnimation_ = AnimationUtils::StartAnimation(
215         option,
216         [weak = WeakClaim(this), fgRect]() {
217             auto modifier = weak.Upgrade();
218             CHECK_NULL_VOID(modifier);
219             modifier->SetCrossModeSize(fgRect.GetSize());
220             modifier->SetCrossModeOffset(fgRect.GetOffset());
221         },
222         [weak = WeakClaim(this)]() {
223             auto modifier = weak.Upgrade();
224             CHECK_NULL_VOID(modifier);
225             modifier->SetHoverAnimatingType(HoverAnimationType::NONE);
226         });
227 }
228 
StopHoverAnimation()229 void ScrollBarOverlayModifier::StopHoverAnimation()
230 {
231     if (hoverAnimation_) {
232         AnimationUtils::StopAnimation(hoverAnimation_);
233     }
234 }
235 
StopOpacityAnimation()236 void ScrollBarOverlayModifier::StopOpacityAnimation()
237 {
238     if (opacityAnimation_) {
239         AnimationUtils::StopAnimation(opacityAnimation_);
240     }
241 }
242 
StartOpacityAnimation(OpacityAnimationType opacityAnimationType)243 void ScrollBarOverlayModifier::StartOpacityAnimation(OpacityAnimationType opacityAnimationType)
244 {
245     CHECK_NULL_VOID(opacity_);
246     if (opacityAnimationType != opacityAnimatingType_) {
247         StopOpacityAnimation();
248     } else {
249         return;
250     }
251     if (opacityAnimationType == OpacityAnimationType::APPEAR_WITHOUT_ANIMATION) {
252         opacityAnimatingType_ = OpacityAnimationType::NONE;
253         opacity_->Set(UINT8_MAX);
254         return;
255     }
256     AnimationOption option;
257     option.SetCurve(Curves::SHARP);
258     if (opacityAnimationType == OpacityAnimationType::DISAPPEAR) {
259         if (!isNavDestinationShow_) {
260             opacityAnimatingType_ = OpacityAnimationType::NONE;
261             opacity_->Set(0);
262             return;
263         }
264         option.SetFrameRateRange(AceType::MakeRefPtr<FrameRateRange>(
265             BAR_DISAPPEAR_MIN_FRAME_RATE, BAR_DISAPPEAR_MAX_FRAME_RATE, BAR_DISAPPEAR_FRAME_RATE));
266         option.SetDuration(BAR_DISAPPEAR_DURATION);
267     } else if (opacityAnimationType == OpacityAnimationType::APPEAR) {
268         option.SetDuration(BAR_APPEAR_DURATION);
269     }
270     opacityAnimatingType_ = opacityAnimationType;
271     opacityAnimation_ = AnimationUtils::StartAnimation(
272         option,
273         [weak = WeakClaim(this)]() {
274             auto modifier = weak.Upgrade();
275             CHECK_NULL_VOID(modifier);
276             if (modifier->opacityAnimatingType_ == OpacityAnimationType::DISAPPEAR) {
277                 modifier->opacity_->Set(0);
278             } else if (modifier->opacityAnimatingType_ == OpacityAnimationType::APPEAR) {
279                 modifier->opacity_->Set(UINT8_MAX);
280             }
281         },
282         [weak = WeakClaim(this)]() {
283             auto modifier = weak.Upgrade();
284             CHECK_NULL_VOID(modifier);
285             modifier->SetOpacityAnimatingType(OpacityAnimationType::NONE);
286         });
287 }
288 
SetBarColor(Color barColor)289 void ScrollBarOverlayModifier::SetBarColor(Color barColor)
290 {
291     CHECK_NULL_VOID(barColor_);
292     barColor_->Set(barColor);
293 }
294 
GetHoverOffset(const Size & size) const295 Offset ScrollBarOverlayModifier::GetHoverOffset(const Size& size) const
296 {
297     if (positionMode_ == PositionMode::RIGHT) {
298         return Offset(size.Width() - barWidth_->Get(), 0.f);
299     } else if (positionMode_ == PositionMode::BOTTOM) {
300         return Offset(0.f, size.Height() - barHeight_->Get());
301     }
302     return Offset::Zero();
303 }
304 
CheckMainModeNearEqual()305 void ScrollBarOverlayModifier::CheckMainModeNearEqual()
306 {
307     if (isAdaptAnimationStop_) {
308         return;
309     }
310     float MainModeHeight = 0.f;
311     float MainModeOffset = 0.f;
312     if (positionMode_ == PositionMode::BOTTOM) {
313         MainModeHeight = barWidth_->Get();
314         MainModeOffset = barX_->Get();
315     } else {
316         MainModeHeight = barHeight_->Get();
317         MainModeOffset = barY_->Get();
318     }
319     if (NearEqual(lastMainModeHeight_, MainModeHeight, ADAPT_ACCURACY) &&
320         NearEqual(lastMainModeOffset_, MainModeOffset, ADAPT_ACCURACY)) {
321         StopAdaptAnimation();
322     }
323     lastMainModeHeight_ = MainModeHeight;
324     lastMainModeOffset_ = MainModeOffset;
325 }
326 } // namespace OHOS::Ace::NG
327