• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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/progress/render_loading_progress.h"
17 
18 #include "base/log/event_report.h"
19 #include "base/log/log.h"
20 #include "core/animation/curve_animation.h"
21 #include "core/animation/keyframe_animation.h"
22 #include "core/components/progress/progress_theme.h"
23 
24 namespace OHOS::Ace {
25 namespace {
26 
27 constexpr double LOOP_DEGREES = 360.0;
28 constexpr double TAIL_ALPHA_RATIO = 0.82;
29 constexpr int32_t LOOP_DURATION = 1200;
30 constexpr int32_t MOVE_DURATION = LOOP_DURATION / 12; // Comet move without tail.
31 constexpr int32_t TAIL_DURATION = LOOP_DURATION / 4;  // Comet move with tail getting longer.
32 constexpr double DRAG_ANGLE_BEGIN = -15.0;
33 constexpr double DRAG_ANGLE_RANGE = 30.0;
34 constexpr double RING_SCALE_BEGIN = 0.5;
35 constexpr double RING_SCALE_RANGE = 0.5;
36 constexpr double MOVE_START = DRAG_ANGLE_BEGIN + DRAG_ANGLE_RANGE;
37 constexpr double MOVE_END = MOVE_START + LOOP_DEGREES / LOOP_DURATION * MOVE_DURATION;
38 constexpr double TAIL_END = MOVE_END + LOOP_DEGREES / LOOP_DURATION * TAIL_DURATION;
39 constexpr double LOOP_END = TAIL_END + LOOP_DEGREES;
40 constexpr int32_t START_POINT = 0;
41 constexpr int32_t MIDDLE_POINT = 1;
42 constexpr int32_t END_POINT = 2;
43 constexpr double CENTER_POINT = 2.0;
44 const Dimension MODE_SMALL = 16.0_vp;
45 const Dimension MODE_MIDDLE = 40.0_vp;
46 const Dimension MODE_LARGE = 76.0_vp;
47 const Dimension MODE_COMET_RADIUS[] = { 3.0_vp, 3.0_vp, 2.2_vp };
48 const Dimension MODE_RING_WIDTH[] = { 2.8_vp, 1.9_vp, 1.2_vp };
49 const Dimension MODE_RING_BLUR_RADIUS[] = { 0.5_vp, 0.2_vp, 0.1_vp };
50 const Dimension MODE_RING_BG_WIDTH[] = { 3.0_vp, 3.0_vp, 2.0_vp };
51 const Dimension MODE_RING_BG_BLUR_RADIUS[] = { 2.0_vp, 2.0_vp, 2.0_vp };
52 
53 } // namespace
54 
RenderLoadingProgress()55 RenderLoadingProgress::RenderLoadingProgress() : RenderNode(true) {}
56 
Update(const RefPtr<Component> & component)57 void RenderLoadingProgress::Update(const RefPtr<Component>& component)
58 {
59     auto loadingProgress = AceType::DynamicCast<LoadingProgressComponent>(component);
60     if (!loadingProgress) {
61         LOGE("Update with nullptr");
62         EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
63         return;
64     }
65     progressColor_ = loadingProgress->GetProgressColor();
66     moveRatio_ = loadingProgress->GetMoveRatio();
67     cometTailLen_ = loadingProgress->GetCometTailLen();
68     diameterDimension_ = loadingProgress->GetDiameter();
69     ringRadiusDimension_ = loadingProgress->GetRingRadius();
70     orbitRadiusDimension_ = loadingProgress->GetOrbitRadius();
71     MarkNeedLayout();
72 }
73 
UpdateRingAnimation()74 void RenderLoadingProgress::UpdateRingAnimation()
75 {
76     auto ringMove = AceType::MakeRefPtr<KeyframeAnimation<float>>();
77     ringMove->AddListener([weak = WeakClaim(this)](double value) {
78         auto loading = weak.Upgrade();
79         if (loading) {
80             loading->ringOffset_.SetY(value * loading->scale_);
81             if (loading->GetVisible() && !loading->GetHidden()) {
82                 loading->MarkNeedRender();
83             }
84         }
85     });
86     double moveRange = ringRadius_ * moveRatio_ * 2.0;
87     auto keyframe1 = AceType::MakeRefPtr<Keyframe<float>>(0.0f, 0.0f);
88     auto keyframe2 = AceType::MakeRefPtr<Keyframe<float>>(0.25f, -moveRange);
89     auto keyframe3 = AceType::MakeRefPtr<Keyframe<float>>(0.75f, moveRange);
90     auto keyframe4 = AceType::MakeRefPtr<Keyframe<float>>(1.0f, 0.0f);
91     keyframe2->SetCurve(AceType::MakeRefPtr<CubicCurve>(0.0f, 0.0f, 0.67f, 1.0f));
92     keyframe3->SetCurve(AceType::MakeRefPtr<CubicCurve>(0.33f, 0.0f, 0.67f, 1.0f));
93     keyframe4->SetCurve(AceType::MakeRefPtr<CubicCurve>(0.33f, 0.0f, 1.0f, 1.0f));
94     ringMove->AddKeyframe(keyframe1);
95     ringMove->AddKeyframe(keyframe2);
96     ringMove->AddKeyframe(keyframe3);
97     ringMove->AddKeyframe(keyframe4);
98     ringController_->ClearInterpolators();
99     ringController_->AddInterpolator(ringMove);
100     ringController_->SetIteration(ANIMATION_REPEAT_INFINITE);
101     ringController_->SetDuration(LOOP_DURATION);
102     if (GetVisible() && !GetHidden()) {
103         ringController_->Play();
104     }
105 }
106 
UpdateCometAnimation()107 void RenderLoadingProgress::UpdateCometAnimation()
108 {
109     auto cometMoveStart = AceType::MakeRefPtr<CurveAnimation<float>>(MOVE_START, MOVE_END,
110         AceType::MakeRefPtr<CubicCurve>(0.6f, 0.2f, 1.0f, 1.0f));
111     cometMoveStart->AddListener([weak = AceType::WeakClaim(this)](double value) {
112         auto loading = weak.Upgrade();
113         if (loading) {
114             CometParam para;
115             para.angular = value;
116             loading->cometParams_.clear();
117             loading->cometParams_.emplace_back(para);
118             loading->UpdateCometParams();
119         }
120     });
121     cometController_->ClearInterpolators();
122     cometController_->AddInterpolator(cometMoveStart);
123     cometController_->SetIteration(1);
124     cometController_->SetDuration(MOVE_DURATION);
125     cometController_->SetFillMode(FillMode::FORWARDS);
126     cometController_->ClearStopListeners();
127     moveStopId_ = cometController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
128         auto loading = weak.Upgrade();
129         if (loading) {
130             loading->DoCometTailAnimation();
131         }
132     });
133     if (GetVisible() && !GetHidden()) {
134         cometController_->Play();
135     }
136 }
137 
DoCometTailAnimation()138 void RenderLoadingProgress::DoCometTailAnimation()
139 {
140     auto cometMoveTail = AceType::MakeRefPtr<CurveAnimation<float>>(0.0, cometTailLen_, Curves::LINEAR);
141     cometMoveTail->AddListener([weak = AceType::WeakClaim(this)](double value) {
142         auto loading = weak.Upgrade();
143         if (loading) {
144             loading->cometCurTail_ = value;
145             if (loading->moveStopId_ != 0 && loading->cometController_) {
146                 loading->cometController_->RemoveStopListener(loading->moveStopId_);
147                 loading->moveStopId_ = 0;
148             }
149         }
150     });
151     auto cometMoveDegree = AceType::MakeRefPtr<CurveAnimation<float>>(MOVE_END, TAIL_END, Curves::LINEAR);
152     cometMoveDegree->AddListener([weak = AceType::WeakClaim(this)](double value) {
153         auto loading = weak.Upgrade();
154         if (loading) {
155             double step = loading->cometTailLen_ / loading->cometCount_;
156             int32_t count = 0;
157             while (count < loading->cometCount_) {
158                 double curStep = std::min(count * step, value);
159                 if (count < (int32_t)loading->cometParams_.size()) {
160                     loading->cometParams_[count].angular = value - curStep;
161                 } else {
162                     CometParam para;
163                     para.angular = value - curStep;
164                     loading->cometParams_.emplace_back(para);
165                 }
166                 if (count * step >= loading->cometCurTail_) {
167                     break;
168                 }
169                 count++;
170             }
171             loading->UpdateCometParams();
172         }
173     });
174     cometController_->ClearInterpolators();
175     cometController_->AddInterpolator(cometMoveTail);
176     cometController_->AddInterpolator(cometMoveDegree);
177     cometController_->SetIteration(1);
178     cometController_->SetDuration(TAIL_DURATION);
179     cometController_->SetFillMode(FillMode::FORWARDS);
180     tailStopId_ = cometController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
181         auto loading = weak.Upgrade();
182         if (loading) {
183             loading->DoCometLoopAnimation();
184         }
185     });
186     if (GetVisible() && !GetHidden()) {
187         cometController_->Play();
188     }
189 }
190 
DoCometLoopAnimation()191 void RenderLoadingProgress::DoCometLoopAnimation()
192 {
193     auto cometLoopDegree = AceType::MakeRefPtr<CurveAnimation<float>>(TAIL_END, LOOP_END, Curves::LINEAR);
194     cometLoopDegree->AddListener([weak = AceType::WeakClaim(this)](double value) {
195         auto loading = weak.Upgrade();
196         if (loading) {
197             int32_t count = 0;
198             double step = loading->cometTailLen_ / loading->cometCount_;
199             for (auto& para : loading->cometParams_) {
200                 para.angular = value - count * step;
201                 count++;
202             }
203             loading->UpdateCometParams();
204             if (loading->tailStopId_ != 0 && loading->cometController_) {
205                 loading->cometController_->RemoveStopListener(loading->tailStopId_);
206                 loading->tailStopId_ = 0;
207             }
208         }
209     });
210     cometController_->ClearInterpolators();
211     cometController_->AddInterpolator(cometLoopDegree);
212     cometController_->SetIteration(ANIMATION_REPEAT_INFINITE);
213     cometController_->SetDuration(LOOP_DURATION);
214     if (GetVisible() && !GetHidden()) {
215         cometController_->Play();
216     }
217 }
218 
UpdateCometParams()219 void RenderLoadingProgress::UpdateCometParams()
220 {
221     if (cometParams_.empty()) {
222         return;
223     }
224     int32_t count = 0;
225     float alpha = 0.0f;
226     for (auto& para : cometParams_) {
227         if (count == 0) { // Update Head Comet Parameter.
228             para.scale = GetCometScaleByDegree(para.angular);
229             para.alpha = floor(UINT8_MAX * GetCometAlphaByDegree(cometParams_[0].angular));
230         } else { // Update Tail Comets Parameter.
231             para.scale = GetCometScaleByDegree(para.angular);
232             para.alpha = floor(alpha);
233         }
234         alpha = para.alpha * TAIL_ALPHA_RATIO;
235         count++;
236     }
237 }
238 
GetCometScaleByDegree(double degree)239 float RenderLoadingProgress::GetCometScaleByDegree(double degree)
240 {
241     // Scale Curve::LINEAR Degrees(  0 - 180) --> Scale(100% -  65%)
242     // Scale Curve::LINEAR Degrees(180 - 360) --> Scale( 65% - 100%)
243     if (degree > 360.0) {
244         degree = degree - 360.0;
245     }
246     if (degree >= 0.0 && degree <= 180.0) {
247         return 1.0 - 0.35 * degree / 180.0;
248     }
249     if (degree >= 180.0 && degree <= 360.0) {
250         return 0.65 + 0.35 * (degree - 180.0) / 180.0;
251     }
252     return 1.0f;
253 }
254 
GetCometAlphaByDegree(double degree)255 float RenderLoadingProgress::GetCometAlphaByDegree(double degree)
256 {
257     // Alpha Curve::LINEAR Degrees(  0 -  15) --> Alpha(100% - 100%)
258     // Alpha Curve::LINEAR Degrees( 15 - 180) --> Scale(100% -  20%)
259     // Alpha Curve::LINEAR Degrees(180 - 345) --> Scale( 20% - 100%)
260     // Alpha Curve::LINEAR Degrees(345 - 360) --> Scale(100% - 100%)
261     if (degree > 360.0) {
262         degree = degree - 360.0;
263     }
264     if (degree >= 15.0 && degree <= 180.0) {
265         return 1.0 - 0.8 * (degree - 15.0) / (180.0 - 15.0);
266     } else if (degree >= 180.0 && degree <= 345.0) {
267         return 0.2 + 0.8 * (degree - 180.0) / (345.0 - 180.0);
268     } else {
269         return 1.0f;
270     }
271 }
272 
SetLoadingMode(int32_t mode)273 void RenderLoadingProgress::SetLoadingMode(int32_t mode)
274 {
275     if (loadingMode_ == mode) {
276         return;
277     }
278 
279     LOGI("SetLoadingMode to %{public}d", mode);
280     loadingMode_ = mode;
281     MarkNeedLayout();
282     if (loadingMode_ != MODE_DRAG) {
283         return;
284     }
285     if (ringController_ && cometController_) {
286         ringController_->Stop();
287         cometController_->Stop();
288         ringController_->ClearStopListeners();
289         cometController_->ClearStopListeners();
290         ringController_->Finish();
291         cometController_->Finish();
292         ringController_ = nullptr;
293         cometController_ = nullptr;
294         moveStopId_ = 0;
295         tailStopId_ = 0;
296     }
297 }
298 
SetDragRange(double minDistance,double maxDistance)299 void RenderLoadingProgress::SetDragRange(double minDistance, double maxDistance)
300 {
301     minDistance_ = minDistance;
302     maxDistance_ = maxDistance;
303 }
304 
SetDragDistance(double distance)305 void RenderLoadingProgress::SetDragDistance(double distance)
306 {
307     distance = std::clamp(distance, minDistance_, maxDistance_);
308     if (NearEqual(curDistance_, distance)) {
309         return;
310     }
311     curDistance_ = distance;
312     double percent = (curDistance_ - minDistance_) / (maxDistance_ - minDistance_);
313     double scale = RING_SCALE_BEGIN + RING_SCALE_RANGE * percent;
314     switch (loadingMode_) {
315         case MODE_LOOP: {
316             return;
317         }
318         case MODE_DRAG: {
319             exitScale_ = 1.0;
320             exitAlpha_ = 1.0;
321             dragScale_ = scale;
322             dragAlpha_ = percent;
323             // Update Comet Para when drag distance changed.
324             CometParam para;
325             para.alpha = floor(UINT8_MAX * dragAlpha_);
326             para.angular = DRAG_ANGLE_BEGIN + DRAG_ANGLE_RANGE * percent;
327             if (para.angular < 0.0) {
328                 para.angular = para.angular + 360.0;
329             }
330             cometParams_.clear();
331             cometParams_.emplace_back(para);
332             break;
333         }
334         case MODE_EXIT: {
335             dragScale_ = 1.0;
336             dragAlpha_ = 1.0;
337             exitScale_ = scale;
338             exitAlpha_ = percent;
339             break;
340         }
341         default: {
342             LOGW("Unsupported loading mode:%{public}d.", loadingMode_);
343             break;
344         }
345     }
346     if (GetVisible() && !GetHidden()) {
347         MarkNeedRender();
348     }
349 }
350 
PerformLayout()351 void RenderLoadingProgress::PerformLayout()
352 {
353     // the diameter will be constrain by layout size.
354     diameter_ = NormalizeToPx(diameterDimension_);
355     ringRadius_ = NormalizeToPx(ringRadiusDimension_);
356     orbitRadius_ = NormalizeToPx(orbitRadiusDimension_);
357     Size layoutSize;
358     if (!NearEqual(diameter_, 0.0)) {
359         layoutSize = GetLayoutParam().Constrain(Size(diameter_, diameter_));
360     } else {
361         if (GetLayoutParam().GetMaxSize().IsInfinite()) {
362             double defaultDiameter = 0.0;
363             auto theme = GetTheme<ProgressTheme>();
364             if (theme) {
365                 defaultDiameter = NormalizeToPx(theme->GetLoadingDiameter());
366             }
367             layoutSize = Size(defaultDiameter, defaultDiameter);
368         } else {
369             layoutSize = GetLayoutParam().GetMaxSize();
370         }
371     }
372     SetLayoutSize(layoutSize);
373     UpdateLoadingSize(std::min(layoutSize.Width(), layoutSize.Height()));
374     center_ = Offset(layoutSize.Width() / CENTER_POINT, layoutSize.Height() / CENTER_POINT);
375     scale_ = std::min(layoutSize.Width() / (orbitRadius_ + cometRadius_) / CENTER_POINT,
376         layoutSize.Height() / ringRadius_ / CENTER_POINT);
377     auto pipelineContext = GetContext().Upgrade();
378     if (pipelineContext && loadingMode_ != MODE_DRAG && !ringController_ && !cometController_) {
379         ringController_ = AceType::MakeRefPtr<Animator>(pipelineContext);
380         cometController_ = AceType::MakeRefPtr<Animator>(pipelineContext);
381         UpdateRingAnimation();
382         UpdateCometAnimation();
383         AnimationChanged();
384     }
385 }
386 
UpdateLoadingSize(double diameter)387 void RenderLoadingProgress::UpdateLoadingSize(double diameter)
388 {
389     if (diameter <= NormalizeToPx(MODE_SMALL)) {
390         CalculateValue(START_POINT, START_POINT);
391     } else if (diameter <= NormalizeToPx(MODE_MIDDLE)) {
392         CalculateValue(START_POINT, MIDDLE_POINT,
393             (diameter - NormalizeToPx(MODE_SMALL)) / (NormalizeToPx(MODE_MIDDLE) - NormalizeToPx(MODE_SMALL)));
394     } else if (diameter <= NormalizeToPx(MODE_LARGE)) {
395         CalculateValue(MIDDLE_POINT, END_POINT,
396             (diameter - NormalizeToPx(MODE_MIDDLE)) / (NormalizeToPx(MODE_LARGE) - NormalizeToPx(MODE_MIDDLE)));
397     } else {
398         CalculateValue(END_POINT, END_POINT);
399     }
400 }
401 
CalculateValue(int32_t start,int32_t end,double percent)402 void RenderLoadingProgress::CalculateValue(int32_t start, int32_t end, double percent)
403 {
404     if (start == end) {
405         ringWidth_ = NormalizeToPx(MODE_RING_WIDTH[start]);
406         cometRadius_ = NormalizeToPx(MODE_COMET_RADIUS[start]);
407         ringBlurRadius_ = NormalizeToPx(MODE_RING_BLUR_RADIUS[start]);
408         ringBgWidth_ = NormalizeToPx(MODE_RING_BG_WIDTH[start]);
409         ringBgBlurRadius_ = NormalizeToPx(MODE_RING_BG_BLUR_RADIUS[start]);
410     } else {
411         ringWidth_ = NormalizeToPx(MODE_RING_WIDTH[start] +
412             (MODE_RING_WIDTH[end] - MODE_RING_WIDTH[start]) * percent);
413         cometRadius_ = NormalizeToPx(MODE_COMET_RADIUS[start] +
414             (MODE_COMET_RADIUS[end] - MODE_COMET_RADIUS[start]) * percent);
415         ringBlurRadius_ = NormalizeToPx(MODE_RING_BLUR_RADIUS[start] +
416             (MODE_RING_BLUR_RADIUS[end] - MODE_RING_BLUR_RADIUS[start]) * percent);
417         ringBgWidth_ = NormalizeToPx(MODE_RING_BG_WIDTH[start] +
418             (MODE_RING_BG_WIDTH[end] - MODE_RING_BG_WIDTH[start]) * percent);
419         ringBgBlurRadius_ = NormalizeToPx(MODE_RING_BG_BLUR_RADIUS[start] +
420             (MODE_RING_BG_BLUR_RADIUS[end] - MODE_RING_BG_BLUR_RADIUS[start]) * percent);
421     }
422 }
423 
OnVisibleChanged()424 void RenderLoadingProgress::OnVisibleChanged()
425 {
426     AnimationChanged();
427 }
428 
OnHiddenChanged(bool hidden)429 void RenderLoadingProgress::OnHiddenChanged(bool hidden)
430 {
431     AnimationChanged();
432 }
433 
AnimationChanged()434 void RenderLoadingProgress::AnimationChanged()
435 {
436     if (GetVisible() && !GetHidden()) {
437         if (ringController_) {
438             ringController_->Play();
439         }
440         if (cometController_) {
441             cometController_->Play();
442         }
443     } else {
444         if (ringController_) {
445             ringController_->Pause();
446         }
447         if (cometController_) {
448             cometController_->Pause();
449         }
450     }
451 }
452 
453 } // namespace OHOS::Ace
454