1 /*
2 * Copyright (c) 2022 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/dimension.h"
20 #include "base/memory/ace_type.h"
21 #include "base/utils/utils.h"
22 #include "bridge/common/dom/dom_type.h"
23 #include "core/components/common/properties/animation_option.h"
24 #include "core/components_ng/base/modifier.h"
25 #include "core/components_ng/pattern/loading_progress/loading_progress_utill.h"
26 #include "core/components_ng/pattern/refresh/refresh_animation_state.h"
27 #include "core/components_ng/render/animation_utils.h"
28 #include "core/components_ng/render/drawing.h"
29 #include "core/components_ng/render/drawing_prop_convertor.h"
30 #include "core/components_ng/render/paint.h"
31
32 namespace OHOS::Ace::NG {
33 namespace {
34 constexpr float TOTAL_ANGLE = 360.0f;
35 constexpr float ROTATEX = -116.0f;
36 constexpr float ROTATEY = 30.0f;
37 constexpr float ROTATEZ = 22.0f;
38 constexpr float COUNT = 50.0f;
39 constexpr float HALF = 0.5f;
40 constexpr float DOUBLE = 2.0f;
41 constexpr int32_t RING_ALPHA = 200;
42 constexpr int32_t TOTAL_POINTS_COUNT = 20;
43 constexpr int32_t TAIL_ANIAMTION_DURATION = 400;
44 constexpr int32_t TRANS_DURATION = 100;
45 constexpr float TOTAL_TAIL_LENGTH = 60.0f;
46 constexpr float TAIL_ALPHA_RATIO = 0.82f;
47 constexpr float INITIAL_SIZE_SCALE = 0.825f;
48 constexpr float INITIAL_OPACITY_SCALE = 0.7f;
49 constexpr float COMET_TAIL_ANGLE = 3.0f;
50 constexpr int32_t LOADING_DURATION = 1200;
51 constexpr float FOLLOW_START = 72.0f;
52 constexpr float FOLLOW_SPAN = 10.0f;
53 constexpr float FULL_COUNT = 100.0f;
54 constexpr float STAGE1 = 0.25f;
55 constexpr float STAGE2 = 0.65f;
56 constexpr float STAGE3 = 0.75f;
57 constexpr float STAGE4 = 0.85f;
58 constexpr float STAGE5 = 1.0f;
59 constexpr float OPACITY1 = 0.2f;
60 constexpr float OPACITY2 = 0.7f;
61 constexpr float OPACITY3 = 1.0f;
62 constexpr float SIZE_SCALE1 = 0.65f;
63 constexpr float SIZE_SCALE2 = 0.825f;
64 constexpr float SIZE_SCALE3 = 0.93f;
65 constexpr float MOVE_STEP = 0.06f;
66 constexpr float TRANS_OPACITY_SPAN = 0.3f;
67 constexpr float FULL_OPACITY = 255.0f;
68 constexpr float TWO = 2.0f;
69 } // namespace
LoadingProgressModifier(LoadingProgressOwner loadingProgressOwner)70 LoadingProgressModifier::LoadingProgressModifier(LoadingProgressOwner loadingProgressOwner)
71 : date_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
72 color_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor::BLUE)),
73 centerDeviation_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
74 cometOpacity_(AceType::MakeRefPtr<AnimatablePropertyFloat>(INITIAL_OPACITY_SCALE)),
75 cometSizeScale_(AceType::MakeRefPtr<AnimatablePropertyFloat>(INITIAL_SIZE_SCALE)),
76 cometTailLen_(AceType::MakeRefPtr<AnimatablePropertyFloat>(TOTAL_TAIL_LENGTH)),
77 sizeScale_(AceType::MakeRefPtr<AnimatablePropertyFloat>(1.0f)),
78 loadingProgressOwner_(loadingProgressOwner)
79 {
80 AttachProperty(date_);
81 AttachProperty(color_);
82 AttachProperty(centerDeviation_);
83 AttachProperty(cometOpacity_);
84 AttachProperty(cometSizeScale_);
85 AttachProperty(cometTailLen_);
86 AttachProperty(sizeScale_);
87 };
88
onDraw(DrawingContext & context)89 void LoadingProgressModifier::onDraw(DrawingContext& context)
90 {
91 float date = date_->Get();
92 auto diameter = std::min(context.width, context.height);
93 RingParam ringParam;
94 ringParam.strokeWidth = LoadingProgressUtill::GetRingStrokeWidth(diameter) * sizeScale_->Get();
95 ringParam.radius = LoadingProgressUtill::GetRingRadius(diameter) * sizeScale_->Get();
96 ringParam.movement =
97 (ringParam.radius * DOUBLE + ringParam.strokeWidth) * centerDeviation_->Get() * sizeScale_->Get();
98
99 CometParam cometParam;
100 cometParam.radius = LoadingProgressUtill::GetCometRadius(diameter) * sizeScale_->Get();
101 cometParam.alphaScale = cometOpacity_->Get();
102 cometParam.sizeScale = cometSizeScale_->Get();
103 cometParam.pointCount = GetCometNumber();
104
105 auto orbitRadius = LoadingProgressUtill::GetOrbitRadius(diameter) * sizeScale_->Get();
106 if (date > COUNT) {
107 DrawRing(context, ringParam);
108 DrawOrbit(context, cometParam, orbitRadius, date);
109 } else {
110 DrawOrbit(context, cometParam, orbitRadius, date);
111 DrawRing(context, ringParam);
112 }
113 }
114
DrawRing(DrawingContext & context,const RingParam & ringParam)115 void LoadingProgressModifier::DrawRing(DrawingContext& context, const RingParam& ringParam)
116 {
117 auto& canvas = context.canvas;
118 canvas.Save();
119 RSPen pen;
120 auto ringColor = color_->Get();
121 pen.SetColor(ToRSColor(Color::FromARGB(RING_ALPHA, ringColor.GetRed(), ringColor.GetGreen(), ringColor.GetBlue())));
122 pen.SetWidth(ringParam.strokeWidth);
123 pen.SetAntiAlias(true);
124 canvas.AttachPen(pen);
125 canvas.DrawCircle({ context.width * HALF, context.height * HALF + ringParam.movement }, ringParam.radius);
126 canvas.DetachPen();
127 canvas.Restore();
128 }
129
DrawOrbit(DrawingContext & context,const CometParam & cometParam,float orbitRadius,float date)130 void LoadingProgressModifier::DrawOrbit(
131 DrawingContext& context, const CometParam& cometParam, float orbitRadius, float date)
132 {
133 auto pointCounts = cometParam.pointCount;
134 auto& canvas = context.canvas;
135 float width_ = context.width;
136 float height_ = context.height;
137 double angle = TOTAL_ANGLE * date / FULL_COUNT;
138 auto* camera_ = new RSCamera3D();
139 camera_->Save();
140 camera_->RotateYDegrees(ROTATEY);
141 camera_->RotateXDegrees(ROTATEX);
142 camera_->RotateZDegrees(ROTATEZ);
143 RSMatrix matrix;
144 camera_->ApplyToMatrix(matrix);
145 camera_->Restore();
146 auto center = RSPoint(width_ / 2, height_ / 2);
147 RSBrush brush;
148 brush.SetAntiAlias(true);
149 canvas.Save();
150 canvas.Translate(center.GetX(), center.GetY());
151 std::vector<RSPoint> points;
152 for (uint32_t i = 0; i < pointCounts; i++) {
153 RSPoint point;
154 float cometAngal = GetCurentCometAngle(angle, pointCounts - i, pointCounts);
155 float rad = cometAngal * PI_NUM / (TOTAL_ANGLE * HALF);
156 point.SetX(std::cos(rad) * orbitRadius);
157 point.SetY(-std::sin(rad) * orbitRadius);
158 points.push_back(point);
159 }
160 std::vector<RSPoint> distPoints(points.size());
161 matrix.MapPoints(distPoints, points, points.size());
162 auto cometColor = color_->Get();
163 float colorAlpha = cometColor.GetAlpha() / FULL_OPACITY;
164 auto baseAlpha = colorAlpha * cometParam.alphaScale;
165 for (uint32_t i = 0; i < distPoints.size(); i++) {
166 RSPoint pointCenter = distPoints[i];
167 float setAlpha = GetCurentCometOpacity(baseAlpha, distPoints.size() - i, distPoints.size());
168 if (NearZero(setAlpha)) {
169 continue;
170 }
171 brush.SetColor(
172 ToRSColor(Color::FromRGBO(cometColor.GetRed(), cometColor.GetGreen(), cometColor.GetBlue(), setAlpha)));
173 canvas.AttachBrush(brush);
174 canvas.DrawCircle(pointCenter, cometParam.radius * cometParam.sizeScale);
175 }
176 canvas.DetachBrush();
177 canvas.Restore();
178 }
179
StartRecycleRingAnimation()180 void LoadingProgressModifier::StartRecycleRingAnimation()
181 {
182 auto context = PipelineBase::GetCurrentContext();
183 CHECK_NULL_VOID(context);
184 auto previousStageCurve = AceType::MakeRefPtr<CubicCurve>(0.0f, 0.0f, 0.67f, 1.0f);
185 AnimationOption option;
186 option.SetDuration(LOADING_DURATION);
187 option.SetCurve(previousStageCurve);
188 if (context->IsFormRender()) {
189 LOGI("LoadingProgress is restricted at runtime when form render");
190 option.SetIteration(1);
191 } else {
192 option.SetIteration(-1);
193 }
194 AnimationUtils::OpenImplicitAnimation(option, previousStageCurve, nullptr);
195 auto middleStageCurve = AceType::MakeRefPtr<CubicCurve>(0.33f, 0.0f, 0.67f, 1.0f);
196 AnimationUtils::AddKeyFrame(STAGE1, middleStageCurve, [&]() { centerDeviation_->Set(-1 * MOVE_STEP); });
197 auto latterStageCurve = AceType::MakeRefPtr<CubicCurve>(0.33f, 0.0f, 1.0f, 1.0f);
198 AnimationUtils::AddKeyFrame(STAGE3, latterStageCurve, [&]() { centerDeviation_->Set(MOVE_STEP); });
199 AnimationUtils::AddKeyFrame(STAGE5, latterStageCurve, [&]() { centerDeviation_->Set(0.0f); });
200 AnimationUtils::CloseImplicitAnimation();
201 }
202
StartRecycleCometAnimation()203 void LoadingProgressModifier::StartRecycleCometAnimation()
204 {
205 auto context = PipelineBase::GetCurrentContext();
206 CHECK_NULL_VOID(context);
207 auto curve = AceType::MakeRefPtr<LinearCurve>();
208 AnimationOption option;
209 option.SetDuration(LOADING_DURATION);
210 option.SetCurve(curve);
211 if (context->IsFormRender()) {
212 LOGI("LoadingProgress is restricted at runtime when form render");
213 option.SetIteration(1);
214 } else {
215 option.SetIteration(-1);
216 }
217
218 cometOpacity_->Set(OPACITY2);
219 AnimationUtils::OpenImplicitAnimation(option, curve, nullptr);
220 AnimationUtils::AddKeyFrame(STAGE1, curve, [&]() {
221 cometOpacity_->Set(OPACITY1);
222 cometSizeScale_->Set(SIZE_SCALE1);
223 });
224 AnimationUtils::AddKeyFrame(STAGE2, curve, [&]() {
225 cometOpacity_->Set(OPACITY3);
226 cometSizeScale_->Set(SIZE_SCALE3);
227 });
228 AnimationUtils::AddKeyFrame(STAGE3, curve, [&]() {
229 cometOpacity_->Set(OPACITY3);
230 cometSizeScale_->Set(1.0f);
231 });
232 AnimationUtils::AddKeyFrame(STAGE4, curve, [&]() {
233 cometOpacity_->Set(OPACITY3);
234 cometSizeScale_->Set(SIZE_SCALE3);
235 });
236 AnimationUtils::AddKeyFrame(STAGE5, curve, [&]() {
237 cometOpacity_->Set(OPACITY2);
238 cometSizeScale_->Set(SIZE_SCALE2);
239 });
240 AnimationUtils::CloseImplicitAnimation();
241 }
242
StartCometTailAnimation()243 void LoadingProgressModifier::StartCometTailAnimation()
244 {
245 auto curve = AceType::MakeRefPtr<LinearCurve>();
246 AnimationOption option;
247 option.SetDuration(TAIL_ANIAMTION_DURATION);
248 option.SetIteration(1);
249 option.SetCurve(curve);
250 AnimationUtils::Animate(option, [&]() { cometTailLen_->Set(TOTAL_TAIL_LENGTH); });
251 }
252
GetCurentCometOpacity(float baseOpacity,uint32_t index,uint32_t totalNumber)253 float LoadingProgressModifier::GetCurentCometOpacity(float baseOpacity, uint32_t index, uint32_t totalNumber)
254 {
255 return baseOpacity * std::pow(TAIL_ALPHA_RATIO, std::clamp(index, 1u, totalNumber) - 1);
256 }
257
GetCurentCometAngle(float baseAngle,uint32_t index,uint32_t totalNumber)258 float LoadingProgressModifier::GetCurentCometAngle(float baseAngle, uint32_t index, uint32_t totalNumber)
259 {
260 return std::fmod((baseAngle - (std::clamp(index, 1u, totalNumber) - 1) * COMET_TAIL_ANGLE), TOTAL_ANGLE);
261 }
262
GetCometNumber()263 uint32_t LoadingProgressModifier::GetCometNumber()
264 {
265 CHECK_NULL_RETURN(cometTailLen_, TOTAL_POINTS_COUNT);
266 return static_cast<uint32_t>(cometTailLen_->Get() / COMET_TAIL_ANGLE);
267 }
268
StartRecycle()269 void LoadingProgressModifier::StartRecycle()
270 {
271 auto context = PipelineBase::GetCurrentContext();
272 CHECK_NULL_VOID(context);
273 if (isLoading_) {
274 return;
275 }
276 sizeScale_->Set(1.0f);
277 if (date_) {
278 isLoading_ = true;
279 date_->Set(0.0f);
280 AnimationOption option = AnimationOption();
281 RefPtr<Curve> curve = AceType::MakeRefPtr<LinearCurve>();
282 option.SetDuration(LOADING_DURATION);
283 option.SetDelay(0);
284 option.SetCurve(curve);
285 if (context->IsFormRender()) {
286 LOGI("LoadingProgress is restricted at runtime when form render");
287 option.SetIteration(1);
288 } else {
289 option.SetIteration(-1);
290 }
291
292 AnimationUtils::Animate(option, [&]() { date_->Set(FULL_COUNT); });
293 }
294 cometOpacity_->Set(INITIAL_OPACITY_SCALE);
295 cometSizeScale_->Set(INITIAL_SIZE_SCALE);
296 StartRecycleRingAnimation();
297 StartRecycleCometAnimation();
298 }
299
StartTransToRecycleAnimation()300 void LoadingProgressModifier::StartTransToRecycleAnimation()
301 {
302 auto curve = AceType::MakeRefPtr<CubicCurve>(0.6f, 0.2f, 1.0f, 1.0f);
303 AnimationOption option;
304 option.SetDuration(TRANS_DURATION);
305 option.SetIteration(1);
306 option.SetCurve(curve);
307 AnimationUtils::Animate(
308 option,
309 [&]() {
310 date_->Set(FULL_COUNT);
311 cometOpacity_->Set(1.0 - TRANS_OPACITY_SPAN);
312 cometSizeScale_->Set(INITIAL_SIZE_SCALE);
313 },
314 [&]() { StartRecycle(); });
315 StartCometTailAnimation();
316 }
317
ChangeRefreshFollowData(float refreshFollowRatio)318 void LoadingProgressModifier::ChangeRefreshFollowData(float refreshFollowRatio)
319 {
320 auto ratio = CorrectNormalize(refreshFollowRatio);
321 sizeScale_->Set(std::sqrt(TWO) * HALF + (1.0 - std::sqrt(TWO) * HALF) * ratio);
322 if (isLoading_) {
323 return;
324 }
325 CHECK_NULL_VOID(date_);
326 date_->Set(FOLLOW_START + FOLLOW_SPAN * ratio);
327 cometTailLen_->Set(COMET_TAIL_ANGLE);
328 cometOpacity_->Set(1.0f);
329 cometSizeScale_->Set(1.0f);
330 }
331
CorrectNormalize(float originData)332 float LoadingProgressModifier::CorrectNormalize(float originData)
333 {
334 auto ratio = originData;
335 if (ratio < 0.0f) {
336 ratio = 0.0f;
337 }
338 if (ratio > 1.0f) {
339 ratio = 1.0f;
340 };
341 return ratio;
342 }
343 } // namespace OHOS::Ace::NG
344