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