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