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/loading_progress/loading_progress_modifier.h"
17 #include <algorithm>
18
19 #include "base/geometry/arc.h"
20 #include "base/geometry/dimension.h"
21 #include "base/memory/ace_type.h"
22 #include "base/utils/utils.h"
23 #include "bridge/common/dom/dom_type.h"
24 #include "core/components/common/properties/animation_option.h"
25 #include "core/components/progress/progress_theme.h"
26 #include "core/components_ng/base/modifier.h"
27 #include "core/components_ng/pattern/loading_progress/loading_progress_utill.h"
28 #include "core/components_ng/pattern/refresh/refresh_animation_state.h"
29 #include "core/components_ng/render/animation_utils.h"
30 #include "core/components_ng/render/drawing.h"
31 #include "core/components_ng/render/drawing_prop_convertor.h"
32
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr float TOTAL_ANGLE = 360.0f;
36 constexpr float ROTATEX = -116.0f;
37 constexpr float ROTATEY = 30.0f;
38 constexpr float ROTATEZ = 22.0f;
39 constexpr float COUNT = 50.0f;
40 constexpr float HALF = 0.5f;
41 constexpr float DOUBLE = 2.0f;
42 constexpr int32_t RING_ALPHA = 200;
43 constexpr int32_t TOTAL_POINTS_COUNT = 20;
44 constexpr int32_t TAIL_ANIAMTION_DURATION = 400;
45 constexpr int32_t TRANS_DURATION = 100;
46 constexpr float TOTAL_TAIL_LENGTH = 60.0f;
47 constexpr float TAIL_ALPHA_RATIO = 0.82f;
48 constexpr float INITIAL_SIZE_SCALE = 0.825f;
49 constexpr float INITIAL_OPACITY_SCALE = 0.7f;
50 constexpr float COMET_TAIL_ANGLE = 3.0f;
51 constexpr int32_t LOADING_DURATION = 1200;
52 constexpr float FOLLOW_START = 72.0f;
53 constexpr float FOLLOW_SPAN = 10.0f;
54 constexpr float FULL_COUNT = 100.0f;
55 constexpr float STAGE1 = 0.25f;
56 constexpr float STAGE2 = 0.65f;
57 constexpr float STAGE3 = 0.75f;
58 constexpr float STAGE4 = 0.85f;
59 constexpr float STAGE5 = 1.0f;
60 constexpr float OPACITY1 = 0.2f;
61 constexpr float OPACITY2 = 0.7f;
62 constexpr float OPACITY3 = 1.0f;
63 constexpr float SIZE_SCALE1 = 0.65f;
64 constexpr float SIZE_SCALE2 = 0.825f;
65 constexpr float SIZE_SCALE3 = 0.93f;
66 constexpr float MOVE_STEP = 0.06f;
67 constexpr float TRANS_OPACITY_SPAN = 0.3f;
68 constexpr float FULL_OPACITY = 255.0f;
69 constexpr float TWO = 2.0f;
70 } // namespace
LoadingProgressModifier(LoadingProgressOwner loadingProgressOwner)71 LoadingProgressModifier::LoadingProgressModifier(LoadingProgressOwner loadingProgressOwner)
72 : enableLoading_(AceType::MakeRefPtr<PropertyBool>(true)),
73 offset_(AceType::MakeRefPtr<PropertyOffsetF>(OffsetF())),
74 contentSize_(AceType::MakeRefPtr<PropertySizeF>(SizeF())),
75 date_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
76 color_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor::TRANSPARENT)),
77 centerDeviation_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
78 cometOpacity_(AceType::MakeRefPtr<AnimatablePropertyFloat>(INITIAL_OPACITY_SCALE)),
79 cometSizeScale_(AceType::MakeRefPtr<AnimatablePropertyFloat>(INITIAL_SIZE_SCALE)),
80 cometTailLen_(AceType::MakeRefPtr<AnimatablePropertyFloat>(TOTAL_TAIL_LENGTH)),
81 sizeScale_(AceType::MakeRefPtr<AnimatablePropertyFloat>(1.0f)), loadingProgressOwner_(loadingProgressOwner)
82 {
83 AttachProperty(enableLoading_);
84 AttachProperty(offset_);
85 AttachProperty(contentSize_);
86 AttachProperty(date_);
87 AttachProperty(color_);
88 AttachProperty(centerDeviation_);
89 AttachProperty(cometOpacity_);
90 AttachProperty(cometSizeScale_);
91 AttachProperty(cometTailLen_);
92 AttachProperty(sizeScale_);
93 };
94
onDraw(DrawingContext & context)95 void LoadingProgressModifier::onDraw(DrawingContext& context)
96 {
97 if (!enableLoading_->Get()) {
98 return;
99 }
100 float date = date_->Get();
101 auto diameter = std::min(contentSize_->Get().Width(), contentSize_->Get().Height());
102 RingParam ringParam;
103 ringParam.strokeWidth = LoadingProgressUtill::GetRingStrokeWidth(diameter) * sizeScale_->Get();
104 ringParam.radius = LoadingProgressUtill::GetRingRadius(diameter) * sizeScale_->Get();
105 ringParam.movement =
106 (ringParam.radius * DOUBLE + ringParam.strokeWidth) * centerDeviation_->Get() * sizeScale_->Get();
107
108 CometParam cometParam;
109 cometParam.radius = LoadingProgressUtill::GetCometRadius(diameter) * sizeScale_->Get();
110 cometParam.alphaScale = cometOpacity_->Get();
111 cometParam.sizeScale = cometSizeScale_->Get();
112 cometParam.pointCount = GetCometNumber();
113
114 auto orbitRadius = LoadingProgressUtill::GetOrbitRadius(diameter) * sizeScale_->Get();
115 if (date > COUNT) {
116 DrawRing(context, ringParam);
117 DrawOrbit(context, cometParam, orbitRadius, date);
118 } else {
119 DrawOrbit(context, cometParam, orbitRadius, date);
120 DrawRing(context, ringParam);
121 }
122 }
123
DrawRing(DrawingContext & context,const RingParam & ringParam)124 void LoadingProgressModifier::DrawRing(DrawingContext& context, const RingParam& ringParam)
125 {
126 auto& canvas = context.canvas;
127 canvas.Save();
128 RSPen pen;
129 auto ringColor = color_->Get();
130 auto pipeline = PipelineBase::GetCurrentContext();
131 CHECK_NULL_VOID(pipeline);
132 auto progressTheme = pipeline->GetTheme<ProgressTheme>();
133 CHECK_NULL_VOID(progressTheme);
134 auto defaultColor = progressTheme->GetLoadingColor();
135 if (ringColor.GetValue() == defaultColor.GetValue()) {
136 pen.SetColor(
137 ToRSColor(Color::FromARGB(RING_ALPHA, ringColor.GetRed(), ringColor.GetGreen(), ringColor.GetBlue())));
138 } else {
139 pen.SetColor(ToRSColor(
140 Color::FromARGB(ringColor.GetAlpha(), ringColor.GetRed(), ringColor.GetGreen(), ringColor.GetBlue())));
141 }
142 pen.SetWidth(ringParam.strokeWidth);
143 pen.SetAntiAlias(true);
144 canvas.AttachPen(pen);
145 canvas.DrawCircle(
146 { offset_->Get().GetX() + contentSize_->Get().Width() * HALF,
147 offset_->Get().GetY() + contentSize_->Get().Height() * HALF + ringParam.movement },
148 ringParam.radius);
149 canvas.DetachPen();
150 canvas.Restore();
151 }
152
DrawOrbit(DrawingContext & context,const CometParam & cometParam,float orbitRadius,float date)153 void LoadingProgressModifier::DrawOrbit(
154 DrawingContext& context, const CometParam& cometParam, float orbitRadius, float date)
155 {
156 auto pointCounts = cometParam.pointCount;
157 auto& canvas = context.canvas;
158 float width = contentSize_->Get().Width();
159 float height = contentSize_->Get().Height();
160 double angle = TOTAL_ANGLE * date / FULL_COUNT;
161 RSCamera3D camera;
162 camera.Save();
163 camera.RotateYDegrees(ROTATEY);
164 camera.RotateXDegrees(ROTATEX);
165 camera.RotateZDegrees(ROTATEZ);
166 RSMatrix matrix;
167 camera.ApplyToMatrix(matrix);
168 camera.Restore();
169 auto center = RSPoint(offset_->Get().GetX() + width / 2, offset_->Get().GetY() + height / 2);
170 RSBrush brush;
171 brush.SetAntiAlias(true);
172 canvas.Save();
173 canvas.Translate(center.GetX(), center.GetY());
174 std::vector<RSPoint> points;
175 for (uint32_t i = 0; i < pointCounts; i++) {
176 RSPoint point;
177 float cometAngal = GetCurentCometAngle(angle, pointCounts - i, pointCounts);
178 float rad = cometAngal * PI_NUM / (TOTAL_ANGLE * HALF);
179 point.SetX(std::cos(rad) * orbitRadius);
180 point.SetY(-std::sin(rad) * orbitRadius);
181 points.push_back(point);
182 }
183 std::vector<RSPoint> distPoints(points.size());
184 matrix.MapPoints(distPoints, points, points.size());
185 auto cometColor = color_->Get();
186 float colorAlpha = cometColor.GetAlpha() / FULL_OPACITY;
187 auto baseAlpha = colorAlpha * cometParam.alphaScale;
188 for (uint32_t i = 0; i < distPoints.size(); i++) {
189 RSPoint pointCenter = distPoints[i];
190 if (cometColor.GetValue() == Color::FOREGROUND.GetValue()) {
191 brush.SetColor(ToRSColor(cometColor));
192 } else {
193 float setAlpha = GetCurentCometOpacity(baseAlpha, distPoints.size() - i, distPoints.size());
194 if (NearZero(setAlpha)) {
195 continue;
196 }
197 brush.SetColor(
198 ToRSColor(Color::FromRGBO(cometColor.GetRed(), cometColor.GetGreen(), cometColor.GetBlue(), setAlpha)));
199 }
200 canvas.AttachBrush(brush);
201 canvas.DrawCircle(pointCenter, cometParam.radius * cometParam.sizeScale);
202 }
203 canvas.DetachBrush();
204 canvas.Restore();
205 }
206
StartRecycleRingAnimation()207 void LoadingProgressModifier::StartRecycleRingAnimation()
208 {
209 auto context = PipelineBase::GetCurrentContext();
210 CHECK_NULL_VOID(context);
211 auto previousStageCurve = AceType::MakeRefPtr<CubicCurve>(0.0f, 0.0f, 0.67f, 1.0f);
212 AnimationOption option;
213 option.SetDuration(isVisible_ ? LOADING_DURATION : 0);
214 option.SetCurve(previousStageCurve);
215 if (context->IsFormRender()) {
216 LOGI("LoadingProgress is restricted at runtime when form render");
217 option.SetIteration(1);
218 } else {
219 option.SetIteration(-1);
220 }
221 AnimationUtils::OpenImplicitAnimation(option, previousStageCurve, nullptr);
222 auto middleStageCurve = AceType::MakeRefPtr<CubicCurve>(0.33f, 0.0f, 0.67f, 1.0f);
223 AnimationUtils::AddKeyFrame(
224 STAGE1, middleStageCurve, [weakCenterDeviation = AceType::WeakClaim(AceType::RawPtr(centerDeviation_))]() {
225 auto centerDeviation = weakCenterDeviation.Upgrade();
226 CHECK_NULL_VOID(centerDeviation);
227 centerDeviation->Set(-1 * MOVE_STEP);
228 });
229 auto latterStageCurve = AceType::MakeRefPtr<CubicCurve>(0.33f, 0.0f, 1.0f, 1.0f);
230 AnimationUtils::AddKeyFrame(
231 STAGE3, latterStageCurve, [weakCenterDeviation = AceType::WeakClaim(AceType::RawPtr(centerDeviation_))]() {
232 auto centerDeviation = weakCenterDeviation.Upgrade();
233 CHECK_NULL_VOID(centerDeviation);
234 centerDeviation->Set(MOVE_STEP);
235 });
236 AnimationUtils::AddKeyFrame(
237 STAGE5, latterStageCurve, [weakCenterDeviation = AceType::WeakClaim(AceType::RawPtr(centerDeviation_))]() {
238 auto centerDeviation = weakCenterDeviation.Upgrade();
239 CHECK_NULL_VOID(centerDeviation);
240 centerDeviation->Set(0.0f);
241 });
242 AnimationUtils::CloseImplicitAnimation();
243 }
244
StartRecycleCometAnimation()245 void LoadingProgressModifier::StartRecycleCometAnimation()
246 {
247 auto context = PipelineBase::GetCurrentContext();
248 CHECK_NULL_VOID(context);
249 auto curve = AceType::MakeRefPtr<LinearCurve>();
250 AnimationOption option;
251 option.SetDuration(isVisible_ ? LOADING_DURATION : 0);
252 option.SetCurve(curve);
253 if (context->IsFormRender()) {
254 LOGI("LoadingProgress is restricted at runtime when form render");
255 option.SetIteration(1);
256 } else {
257 option.SetIteration(-1);
258 }
259
260 cometOpacity_->Set(OPACITY2);
261 AnimationUtils::OpenImplicitAnimation(option, curve, nullptr);
262 AnimationUtils::AddKeyFrame(STAGE1, curve,
263 [weakCometOpacity = AceType::WeakClaim(AceType::RawPtr(cometOpacity_)),
264 weakCometSizeScale = AceType::WeakClaim(AceType::RawPtr(cometSizeScale_))]() {
265 auto cometOpacity = weakCometOpacity.Upgrade();
266 if (cometOpacity) {
267 cometOpacity->Set(OPACITY1);
268 }
269 auto cometSizeScale = weakCometSizeScale.Upgrade();
270 if (cometSizeScale) {
271 cometSizeScale->Set(SIZE_SCALE1);
272 }
273 });
274 AnimationUtils::AddKeyFrame(STAGE2, curve,
275 [weakCometOpacity = AceType::WeakClaim(AceType::RawPtr(cometOpacity_)),
276 weakCometSizeScale = AceType::WeakClaim(AceType::RawPtr(cometSizeScale_))]() {
277 auto cometOpacity = weakCometOpacity.Upgrade();
278 if (cometOpacity) {
279 cometOpacity->Set(OPACITY3);
280 }
281 auto cometSizeScale = weakCometSizeScale.Upgrade();
282 if (cometSizeScale) {
283 cometSizeScale->Set(SIZE_SCALE3);
284 }
285 });
286 AnimationUtils::AddKeyFrame(STAGE3, curve,
287 [weakCometOpacity = AceType::WeakClaim(AceType::RawPtr(cometOpacity_)),
288 weakCometSizeScale = AceType::WeakClaim(AceType::RawPtr(cometSizeScale_))]() {
289 auto cometOpacity = weakCometOpacity.Upgrade();
290 if (cometOpacity) {
291 cometOpacity->Set(OPACITY3);
292 }
293 auto cometSizeScale = weakCometSizeScale.Upgrade();
294 if (cometSizeScale) {
295 cometSizeScale->Set(1.0f);
296 }
297 });
298 AnimationUtils::AddKeyFrame(STAGE4, curve,
299 [weakCometOpacity = AceType::WeakClaim(AceType::RawPtr(cometOpacity_)),
300 weakCometSizeScale = AceType::WeakClaim(AceType::RawPtr(cometSizeScale_))]() {
301 auto cometOpacity = weakCometOpacity.Upgrade();
302 if (cometOpacity) {
303 cometOpacity->Set(OPACITY3);
304 }
305 auto cometSizeScale = weakCometSizeScale.Upgrade();
306 if (cometSizeScale) {
307 cometSizeScale->Set(SIZE_SCALE3);
308 }
309 });
310 AnimationUtils::AddKeyFrame(STAGE5, curve,
311 [weakCometOpacity = AceType::WeakClaim(AceType::RawPtr(cometOpacity_)),
312 weakCometSizeScale = AceType::WeakClaim(AceType::RawPtr(cometSizeScale_))]() {
313 auto cometOpacity = weakCometOpacity.Upgrade();
314 if (cometOpacity) {
315 cometOpacity->Set(OPACITY2);
316 }
317 auto cometSizeScale = weakCometSizeScale.Upgrade();
318 if (cometSizeScale) {
319 cometSizeScale->Set(SIZE_SCALE2);
320 }
321 });
322 AnimationUtils::CloseImplicitAnimation();
323 }
324
StartCometTailAnimation()325 void LoadingProgressModifier::StartCometTailAnimation()
326 {
327 auto curve = AceType::MakeRefPtr<LinearCurve>();
328 AnimationOption option;
329 option.SetDuration(TAIL_ANIAMTION_DURATION);
330 option.SetIteration(1);
331 option.SetCurve(curve);
332 AnimationUtils::Animate(option, [weakCometTailLen = AceType::WeakClaim(AceType::RawPtr(cometTailLen_))]() {
333 auto cometTailLen = weakCometTailLen.Upgrade();
334 CHECK_NULL_VOID(cometTailLen);
335 cometTailLen->Set(TOTAL_TAIL_LENGTH);
336 });
337 }
338
GetCurentCometOpacity(float baseOpacity,uint32_t index,uint32_t totalNumber)339 float LoadingProgressModifier::GetCurentCometOpacity(float baseOpacity, uint32_t index, uint32_t totalNumber)
340 {
341 return baseOpacity * std::pow(TAIL_ALPHA_RATIO, std::clamp(index, 1u, totalNumber) - 1);
342 }
343
GetCurentCometAngle(float baseAngle,uint32_t index,uint32_t totalNumber)344 float LoadingProgressModifier::GetCurentCometAngle(float baseAngle, uint32_t index, uint32_t totalNumber)
345 {
346 return std::fmod((baseAngle - (std::clamp(index, 1u, totalNumber) - 1) * COMET_TAIL_ANGLE), TOTAL_ANGLE);
347 }
348
GetCometNumber()349 uint32_t LoadingProgressModifier::GetCometNumber()
350 {
351 CHECK_NULL_RETURN(cometTailLen_, TOTAL_POINTS_COUNT);
352 return static_cast<uint32_t>(cometTailLen_->Get() / COMET_TAIL_ANGLE);
353 }
354
StartRecycle()355 void LoadingProgressModifier::StartRecycle()
356 {
357 auto context = PipelineBase::GetCurrentContext();
358 CHECK_NULL_VOID(context);
359 if (isLoading_) {
360 return;
361 }
362 sizeScale_->Set(1.0f);
363 if (date_) {
364 isLoading_ = true;
365 date_->Set(0.0f);
366 AnimationOption option = AnimationOption();
367 RefPtr<Curve> curve = AceType::MakeRefPtr<LinearCurve>();
368 LOGD("Loading StartRecycle Visible %d", isVisible_);
369 option.SetDuration(isVisible_ ? LOADING_DURATION : 0);
370 option.SetDelay(0);
371 option.SetCurve(curve);
372 if (context->IsFormRender()) {
373 LOGI("LoadingProgress is restricted at runtime when form render");
374 option.SetIteration(1);
375 } else {
376 option.SetIteration(-1);
377 }
378 AnimationUtils::Animate(option, [weakDate = AceType::WeakClaim(AceType::RawPtr(date_))]() {
379 auto date = weakDate.Upgrade();
380 CHECK_NULL_VOID(date);
381 date->Set(FULL_COUNT);
382 });
383 }
384 cometOpacity_->Set(INITIAL_OPACITY_SCALE);
385 cometSizeScale_->Set(INITIAL_SIZE_SCALE);
386 // ring up and down shift animation
387 StartRecycleRingAnimation();
388 // comet's circle Color transparency and sizeScale animation
389 StartRecycleCometAnimation();
390 }
391
StartTransToRecycleAnimation()392 void LoadingProgressModifier::StartTransToRecycleAnimation()
393 {
394 auto curve = AceType::MakeRefPtr<CubicCurve>(0.6f, 0.2f, 1.0f, 1.0f);
395 AnimationOption option;
396 option.SetDuration(TRANS_DURATION);
397 option.SetIteration(1);
398 option.SetCurve(curve);
399 AnimationUtils::Animate(
400 option,
401 [weakDate = AceType::WeakClaim(AceType::RawPtr(date_)),
402 weakCometOpacity = AceType::WeakClaim(AceType::RawPtr(cometOpacity_)),
403 weakCometSizeScale = AceType::WeakClaim(AceType::RawPtr(cometSizeScale_))]() {
404 auto date = weakDate.Upgrade();
405 if (date) {
406 date->Set(FULL_COUNT);
407 }
408 auto cometOpacity = weakCometOpacity.Upgrade();
409 if (cometOpacity) {
410 cometOpacity->Set(1.0 - TRANS_OPACITY_SPAN);
411 }
412 auto cometSizeScale = weakCometSizeScale.Upgrade();
413 if (cometSizeScale) {
414 cometSizeScale->Set(INITIAL_SIZE_SCALE);
415 }
416 },
417 [weak = AceType::WeakClaim(this)]() {
418 auto modify = weak.Upgrade();
419 CHECK_NULL_VOID(modify);
420 modify->StartRecycle();
421 });
422 StartCometTailAnimation();
423 }
424
ChangeRefreshFollowData(float refreshFollowRatio)425 void LoadingProgressModifier::ChangeRefreshFollowData(float refreshFollowRatio)
426 {
427 auto ratio = CorrectNormalize(refreshFollowRatio);
428 sizeScale_->Set(std::sqrt(TWO) * HALF + (1.0 - std::sqrt(TWO) * HALF) * ratio);
429 if (isLoading_) {
430 return;
431 }
432 CHECK_NULL_VOID(date_);
433 date_->Set(FOLLOW_START + FOLLOW_SPAN * ratio);
434 cometTailLen_->Set(COMET_TAIL_ANGLE);
435 cometOpacity_->Set(1.0f);
436 cometSizeScale_->Set(1.0f);
437 }
438
CorrectNormalize(float originData)439 float LoadingProgressModifier::CorrectNormalize(float originData)
440 {
441 auto ratio = originData;
442 if (ratio < 0.0f) {
443 ratio = 0.0f;
444 }
445 if (ratio > 1.0f) {
446 ratio = 1.0f;
447 };
448 return ratio;
449 }
450 } // namespace OHOS::Ace::NG
451