1 /*
2 * Copyright (c) 2023-2024 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/progress/progress_modifier.h"
17
18 #include "base/geometry/ng/offset_t.h"
19 #include "base/geometry/ng/size_t.h"
20 #include "base/utils/utils.h"
21 #include "core/common/container.h"
22 #include "core/components/progress/progress_theme.h"
23 #include "core/components_ng/base/modifier.h"
24 #include "core/components_ng/render/drawing.h"
25 #include "core/components_ng/render/drawing_prop_convertor.h"
26 #include "core/pipeline/pipeline_base.h"
27
28 namespace OHOS::Ace::NG {
29 namespace {
30 constexpr int32_t INT32_TWO = 2;
31 constexpr int32_t ANGLE_45 = 45;
32 constexpr int32_t ANGLE_90 = 90;
33 constexpr int32_t ANGLE_180 = 180;
34 constexpr int32_t ANGLE_270 = 270;
35 constexpr int32_t ANGLE_360 = 360;
36 constexpr int32_t TIME_2000 = 2000; // 2000 ms
37 constexpr int32_t TIME_4000 = 4000; // 4000 ms
38 constexpr int32_t LOADING_ANIMATION_DURATION = 2000;
39 constexpr float DEFAULT_MAX_VALUE = 100.0f;
40 constexpr float DEFAULT_SCALE_WIDTH = 10.0f;
41 constexpr int32_t DEFAULT_SCALE_COUNT = 100;
42 constexpr double DEFAULT_CAPSULE_BORDER_WIDTH = 0.0;
43 constexpr float FLOAT_ZERO_FIVE = 0.5f;
44 constexpr float FLOAT_TWO_ZERO = 2.0f;
45 constexpr float SPRING_MOTION_RESPONSE = 0.314f;
46 constexpr float SPRING_MOTION_DAMPING_FRACTION = 0.95f;
47 constexpr Dimension SWEEP_WIDTH = 80.0_vp;
48 constexpr float RING_SHADOW_OFFSET_X = 5.0f;
49 constexpr float RING_SHADOW_OFFSET_Y = 5.0f;
50 constexpr float RING_SHADOW_BLUR_RADIUS_MIN = 5.0f;
51 constexpr float RING_SHADOW_VALID_RADIUS_MIN = 10.0f;
52 constexpr float RING_SHADOW_OPACITY = 0.4f;
53 constexpr Dimension LINEAR_SWEEPING_LEN = 80.0_vp;
54 } // namespace
ProgressModifier(const ProgressAnimatableProperty & progressAnimatableProperty_)55 ProgressModifier::ProgressModifier(const ProgressAnimatableProperty& progressAnimatableProperty_)
56 : strokeWidth_(AceType::MakeRefPtr<AnimatablePropertyFloat>(progressAnimatableProperty_.strokeWidth)),
57 color_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(progressAnimatableProperty_.color))),
58 bgColor_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(progressAnimatableProperty_.bgColor))),
59 borderColor_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(progressAnimatableProperty_.borderColor))),
60 value_(AceType::MakeRefPtr<AnimatablePropertyFloat>(progressAnimatableProperty_.value)),
61 ringProgressColors_(AceType::MakeRefPtr<AnimatablePropertyVectorColor>(
62 GradientArithmetic(progressAnimatableProperty_.ringProgressColor))),
63 sweepingDate_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
64 trailingHeadDate_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
65 trailingTailDate_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
66 strokeRadius_(AceType::MakeRefPtr<AnimatablePropertyFloat>(progressAnimatableProperty_.strokeRadius)),
67 offset_(AceType::MakeRefPtr<PropertyOffsetF>(OffsetF())),
68 contentSize_(AceType::MakeRefPtr<PropertySizeF>(SizeF())),
69 maxValue_(AceType::MakeRefPtr<PropertyFloat>(DEFAULT_MAX_VALUE)),
70 scaleWidth_(AceType::MakeRefPtr<PropertyFloat>(DEFAULT_SCALE_WIDTH)),
71 scaleCount_(AceType::MakeRefPtr<PropertyInt>(DEFAULT_SCALE_COUNT)),
72 progressType_(AceType::MakeRefPtr<PropertyInt>(static_cast<int32_t>(ProgressType::LINEAR))),
73 capsuleBorderWidth_(AceType::MakeRefPtr<PropertyFloat>(DEFAULT_CAPSULE_BORDER_WIDTH)),
74 sweepEffect_(AceType::MakeRefPtr<PropertyBool>(false)),
75 ringSweepEffect_(AceType::MakeRefPtr<PropertyBool>(false)),
76 linearSweepEffect_(AceType::MakeRefPtr<PropertyBool>(false)),
77 paintShadow_(AceType::MakeRefPtr<PropertyBool>(false)),
78 progressStatus_(AceType::MakeRefPtr<PropertyInt>(static_cast<int32_t>(ProgressStatus::PROGRESSING))),
79 isItalic_(AceType::MakeRefPtr<PropertyBool>(false)),
80 smoothEffect_(AceType::MakeRefPtr<PropertyBool>(true)),
81 useContentModifier_(AceType::MakeRefPtr<PropertyBool>(false)),
82 isRightToLeft_(AceType::MakeRefPtr<PropertyBool>(false))
83 {
84 AttachProperty(strokeWidth_);
85 AttachProperty(color_);
86 AttachProperty(bgColor_);
87 AttachProperty(borderColor_);
88 AttachProperty(maxValue_);
89 AttachProperty(value_);
90 AttachProperty(scaleWidth_);
91 AttachProperty(scaleCount_);
92 AttachProperty(progressType_);
93 AttachProperty(capsuleBorderWidth_);
94 AttachProperty(sweepEffect_);
95 AttachProperty(trailingHeadDate_);
96 AttachProperty(trailingTailDate_);
97 AttachProperty(strokeRadius_);
98
99 AttachProperty(ringProgressColors_);
100 AttachProperty(sweepingDate_);
101 AttachProperty(paintShadow_);
102 AttachProperty(progressStatus_);
103 AttachProperty(ringSweepEffect_);
104 AttachProperty(linearSweepEffect_);
105 AttachProperty(isItalic_);
106 AttachProperty(smoothEffect_);
107 AttachProperty(isRightToLeft_);
108 }
109
onDraw(DrawingContext & context)110 void ProgressModifier::onDraw(DrawingContext& context)
111 {
112 if (useContentModifier_->Get()) {
113 return;
114 }
115 ContentDrawWithFunction(context);
116 }
117
SetUseContentModifier(bool useContentModifier)118 void ProgressModifier::SetUseContentModifier(bool useContentModifier)
119 {
120 if (useContentModifier_) {
121 useContentModifier_->Set(useContentModifier);
122 }
123 }
124
SetStrokeWidth(float width)125 void ProgressModifier::SetStrokeWidth(float width)
126 {
127 CHECK_NULL_VOID(strokeWidth_);
128 strokeWidth_->Set(width);
129 }
130
SetColor(LinearColor color)131 void ProgressModifier::SetColor(LinearColor color)
132 {
133 CHECK_NULL_VOID(color_);
134 color_->Set(color);
135 }
136
SetBackgroundColor(LinearColor color)137 void ProgressModifier::SetBackgroundColor(LinearColor color)
138 {
139 CHECK_NULL_VOID(bgColor_);
140 bgColor_->Set(color);
141 }
142
SetBorderColor(LinearColor color)143 void ProgressModifier::SetBorderColor(LinearColor color)
144 {
145 CHECK_NULL_VOID(borderColor_);
146 borderColor_->Set(color);
147 }
148
SetProgressType(ProgressType type)149 void ProgressModifier::SetProgressType(ProgressType type)
150 {
151 CHECK_NULL_VOID(progressType_);
152 progressType_->Set(static_cast<int32_t>(type));
153 }
154
ProcessSweepingAnimation(ProgressType type,float value)155 void ProgressModifier::ProcessSweepingAnimation(ProgressType type, float value)
156 {
157 if (!isVisible_) {
158 return;
159 }
160
161 switch (type) {
162 case ProgressType::RING:
163 ProcessRingSweepingAnimation(value);
164 break;
165 case ProgressType::LINEAR:
166 ProcessLinearSweepingAnimation(value);
167 break;
168 case ProgressType::CAPSULE:
169 StartCapsuleSweepingAnimation(value);
170 break;
171 default:
172 break;
173 }
174 }
175
StartCapsuleSweepingAnimation(float value)176 void ProgressModifier::StartCapsuleSweepingAnimation(float value)
177 {
178 auto contentSize = contentSize_->Get();
179 float barLength =
180 GreatOrEqual(contentSize.Width(), contentSize.Height()) ? contentSize.Width() : contentSize.Height();
181 float date = (value / maxValue_->Get()) * barLength + SWEEP_WIDTH.ConvertToPx();
182 float sweepSpeed = barLength / TIME_2000; // It takes 2 seconds to sweep the whole bar length.
183
184 if (!isSweeping_ && sweepEffect_->Get() && isVisible_) {
185 StartCapsuleSweepingAnimationImpl(date, sweepSpeed);
186 } else if (!sweepEffect_->Get() || !isVisible_) {
187 StopSweepingAnimation();
188 } else {
189 dateUpdated_ = !NearEqual(sweepingDateBackup_, date);
190 }
191 sweepingDateBackup_ = date;
192 }
193
StartCapsuleSweepingAnimationImpl(float value,float speed)194 void ProgressModifier::StartCapsuleSweepingAnimationImpl(float value, float speed)
195 {
196 if (!isVisible_ || !sweepEffect_) {
197 return;
198 }
199
200 isSweeping_ = true;
201 sweepingDate_->Set(0.0f);
202 speed = NearZero(speed) ? 1.0f : speed;
203 int32_t time = value / speed;
204 AnimationOption option = AnimationOption();
205 auto motion = AceType::MakeRefPtr<LinearCurve>();
206 option.SetCurve(motion);
207 option.SetIteration(-1);
208 option.SetDuration(time);
209
210 AnimationUtils::Animate(
211 option,
212 [value, id = Container::CurrentId(), weak = WeakClaim(this)]() {
213 ContainerScope scope(id);
214 auto modifier = weak.Upgrade();
215 CHECK_NULL_VOID(modifier);
216 modifier->sweepingDate_->Set(value);
217 },
218 nullptr,
219 [id = Container::CurrentId(), weak = WeakClaim(this), speed]() {
220 ContainerScope scope(id);
221 auto modifier = weak.Upgrade();
222 CHECK_NULL_VOID(modifier);
223 float currentDate = modifier->sweepingDate_->Get();
224 if (modifier->dateUpdated_) {
225 modifier->dateUpdated_ = false;
226 modifier->StopSweepingAnimation(currentDate);
227 modifier->StartContinuousSweepingAnimation(currentDate, modifier->sweepingDateBackup_, speed);
228 auto context = PipelineBase::GetCurrentContext();
229 context->RequestFrame();
230 }
231 });
232 }
233
SetRingProgressColor(const Gradient & color)234 void ProgressModifier::SetRingProgressColor(const Gradient& color)
235 {
236 CHECK_NULL_VOID(ringProgressColors_);
237 ringProgressColors_->Set(GradientArithmetic(color));
238 }
239
SetPaintShadow(bool paintShadow)240 void ProgressModifier::SetPaintShadow(bool paintShadow)
241 {
242 CHECK_NULL_VOID(paintShadow_);
243 paintShadow_->Set(paintShadow);
244 }
245
SetProgressStatus(ProgressStatus status)246 void ProgressModifier::SetProgressStatus(ProgressStatus status)
247 {
248 CHECK_NULL_VOID(progressStatus_);
249 progressStatus_->Set(static_cast<int32_t>(status));
250 if (status == ProgressStatus::LOADING) {
251 StartRingLoadingAnimation();
252 }
253 }
254
SetIsItalic(bool isItalic)255 void ProgressModifier::SetIsItalic(bool isItalic)
256 {
257 CHECK_NULL_VOID(isItalic_);
258 isItalic_->Set(isItalic);
259 }
260
SetVisible(bool isVisible)261 void ProgressModifier::SetVisible(bool isVisible)
262 {
263 CHECK_NULL_VOID(isVisible_ != isVisible);
264 isVisible_ = isVisible;
265
266 if (!isVisible) {
267 if (isLoading_) {
268 StopRingLoadingHeadAnimation();
269 StopRingLoadingTailAnimation();
270 }
271
272 if (isSweeping_) {
273 StopSweepingAnimation();
274 }
275 }
276 }
277
SetSmoothEffect(bool value)278 void ProgressModifier::SetSmoothEffect(bool value)
279 {
280 CHECK_NULL_VOID(smoothEffect_);
281 smoothEffect_->Set(value);
282 }
283
StartRingLoadingAnimation()284 void ProgressModifier::StartRingLoadingAnimation()
285 {
286 if (!isLoading_ && isVisible_) {
287 isLoading_ = true;
288 StartRingLoadingHeadAnimation();
289 StartRingLoadingTailAnimation();
290 }
291 }
292
StartRingLoadingHeadAnimation()293 void ProgressModifier::StartRingLoadingHeadAnimation()
294 {
295 auto context = PipelineBase::GetCurrentContext();
296 CHECK_NULL_VOID(context);
297 bool isFormRender = context->IsFormRender();
298 AnimationOption optionHead = AnimationOption();
299 auto curveHead = AceType::MakeRefPtr<TailingHeadCurve>();
300 optionHead.SetDuration(LOADING_ANIMATION_DURATION);
301 optionHead.SetCurve(curveHead);
302 optionHead.SetIteration(isFormRender ? 1 : -1);
303 AnimationUtils::Animate(
304 optionHead, [&]() { trailingHeadDate_->Set(ANGLE_360); }, nullptr,
305 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
306 ContainerScope scope(id);
307 auto modifier = weak.Upgrade();
308 CHECK_NULL_VOID(modifier);
309 if (static_cast<ProgressStatus>(modifier->progressStatus_->Get()) == ProgressStatus::PROGRESSING) {
310 modifier->StopRingLoadingHeadAnimation();
311 }
312 });
313 }
314
StartRingLoadingTailAnimation()315 void ProgressModifier::StartRingLoadingTailAnimation()
316 {
317 auto context = PipelineBase::GetCurrentContext();
318 CHECK_NULL_VOID(context);
319 bool isFormRender = context->IsFormRender();
320 AnimationOption optionTail = AnimationOption();
321 auto curveTail = AceType::MakeRefPtr<CubicCurve>(0.33f, 0.00f, 0.66f, 0.10f);
322 optionTail.SetDuration(LOADING_ANIMATION_DURATION);
323 optionTail.SetCurve(curveTail);
324 optionTail.SetIteration(isFormRender ? 1 : -1);
325 AnimationUtils::Animate(
326 optionTail,
327 [&]() { trailingTailDate_->Set(ANGLE_360); },
328 nullptr,
329 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
330 ContainerScope scope(id);
331 auto modifier = weak.Upgrade();
332 CHECK_NULL_VOID(modifier);
333 if (static_cast<ProgressStatus>(modifier->progressStatus_->Get()) == ProgressStatus::PROGRESSING) {
334 modifier->StopRingLoadingTailAnimation();
335 if (GreatOrEqual(modifier->valueBackup_, 0.0f)) {
336 modifier->SetValue(modifier->valueBackup_);
337 }
338 }
339 });
340 }
341
StopRingLoadingHeadAnimation()342 void ProgressModifier::StopRingLoadingHeadAnimation()
343 {
344 AnimationOption option = AnimationOption();
345 option.SetDuration(0);
346 AnimationUtils::Animate(option, [&]() { trailingHeadDate_->Set(0.0f); });
347 }
348
StopRingLoadingTailAnimation()349 void ProgressModifier::StopRingLoadingTailAnimation()
350 {
351 AnimationOption option = AnimationOption();
352 option.SetDuration(0);
353 AnimationUtils::Animate(option, [&]() { trailingTailDate_->Set(0.0f); });
354 isLoading_ = false;
355 }
356
ProcessRingSweepingAnimation(float value)357 void ProgressModifier::ProcessRingSweepingAnimation(float value)
358 {
359 if (NearZero(value) || NearEqual(value, maxValue_->Get())) {
360 StopSweepingAnimation();
361 } else {
362 StartRingSweepingAnimation(value);
363 }
364 }
365
ProcessLinearSweepingAnimation(float value)366 void ProgressModifier::ProcessLinearSweepingAnimation(float value)
367 {
368 if (NearZero(value) || NearEqual(value, maxValue_->Get())) {
369 StopSweepingAnimation();
370 } else {
371 StartLinearSweepingAnimation(value);
372 }
373 }
374
StartRingSweepingAnimation(float value)375 void ProgressModifier::StartRingSweepingAnimation(float value)
376 {
377 float maxValue = NearZero(maxValue_->Get()) ? 1.0f : maxValue_->Get();
378 float date = value / maxValue * ANGLE_360 + ANGLE_45;
379 float additionalAngle = CalcRingProgressAdditionalAngle();
380 date += additionalAngle * 2;
381 float sweepSpeed = float(ANGLE_360) / TIME_4000; // It takes 4 seconds to sweep a circle
382
383 if (!isSweeping_ && ringSweepEffect_->Get()) {
384 StartRingSweepingAnimationImpl(date, sweepSpeed);
385 } else if (!ringSweepEffect_->Get() || NearEqual(valueBackup_, maxValue_->Get())) {
386 StopSweepingAnimation();
387 } else {
388 dateUpdated_ = !NearEqual(sweepingDateBackup_, date);
389 }
390
391 sweepingDateBackup_ = date;
392 }
393
StartRingSweepingAnimationImpl(float date,float speed)394 void ProgressModifier::StartRingSweepingAnimationImpl(float date, float speed)
395 {
396 if (!isVisible_ || !ringSweepEffect_) {
397 return;
398 }
399
400 if (NearEqual(valueBackup_, maxValue_->Get())) {
401 return;
402 }
403
404 auto context = PipelineBase::GetCurrentContext();
405 CHECK_NULL_VOID(context);
406 bool isFormRender = context->IsFormRender();
407 isSweeping_ = true;
408 AnimationOption option = AnimationOption();
409 speed = NearZero(speed) ? 1.0f : speed;
410 int32_t time = date / speed;
411 auto motion = AceType::MakeRefPtr<LinearCurve>();
412 option.SetCurve(motion);
413 option.SetIteration(isFormRender ? 1 : -1);
414 option.SetDuration(time);
415 AnimationUtils::Animate(
416 option,
417 [&]() { sweepingDate_->Set(date); },
418 nullptr,
419 [weak = WeakClaim(this), id = Container::CurrentId(), speed]() {
420 ContainerScope scope(id);
421 auto modifier = weak.Upgrade();
422 CHECK_NULL_VOID(modifier);
423 if (modifier->dateUpdated_) {
424 modifier->dateUpdated_ = false;
425 float currentDate = modifier->sweepingDate_->Get();
426 modifier->StopSweepingAnimation(currentDate);
427 modifier->StartContinuousSweepingAnimation(currentDate, modifier->sweepingDateBackup_, speed);
428 auto context = PipelineBase::GetCurrentContext();
429 context->RequestFrame();
430 }
431 });
432 }
433
StartContinuousSweepingAnimation(float currentDate,float newDate,float speed)434 void ProgressModifier::StartContinuousSweepingAnimation(float currentDate, float newDate, float speed)
435 {
436 if (!isVisible_ || !IsSweepEffectOn()) {
437 return;
438 }
439
440 if (NearEqual(valueBackup_, maxValue_->Get()) &&
441 (ProgressType(progressType_->Get()) == ProgressType::LINEAR ||
442 ProgressType(progressType_->Get()) == ProgressType::RING)) {
443 return;
444 }
445
446 isSweeping_ = true;
447 speed = NearZero(speed) ? 1.0f : speed;
448 int32_t time = (newDate - currentDate) / speed;
449 AnimationOption option = AnimationOption();
450 auto motion = AceType::MakeRefPtr<LinearCurve>();
451 option.SetCurve(motion);
452 option.SetDuration(time);
453 AnimationUtils::Animate(
454 option,
455 [&]() { sweepingDate_->Set(newDate); },
456 [weak = WeakClaim(this), id = Container::CurrentId(), speed]() {
457 ContainerScope scope(id);
458 auto modifier = weak.Upgrade();
459 CHECK_NULL_VOID(modifier);
460 if (!modifier->dateUpdated_) {
461 modifier->sweepingDate_->Set(0.0f);
462 switch (ProgressType(modifier->progressType_->Get())) {
463 case ProgressType::LINEAR:
464 modifier->StartLinearSweepingAnimationImpl(modifier->sweepingDateBackup_, speed);
465 break;
466 case ProgressType::CAPSULE:
467 modifier->StartCapsuleSweepingAnimationImpl(modifier->sweepingDateBackup_, speed);
468 break;
469 case ProgressType::RING:
470 modifier->StartRingSweepingAnimationImpl(modifier->sweepingDateBackup_, speed);
471 break;
472 default:
473 return;
474 }
475 auto context = PipelineBase::GetCurrentContext();
476 context->RequestFrame();
477 } else {
478 modifier->dateUpdated_ = false;
479 float currentDate = modifier->sweepingDate_->Get();
480 modifier->StartContinuousSweepingAnimation(currentDate, modifier->sweepingDateBackup_, speed);
481 auto context = PipelineBase::GetCurrentContext();
482 context->RequestFrame();
483 }
484 });
485 }
486
IsSweepEffectOn()487 bool ProgressModifier::IsSweepEffectOn()
488 {
489 switch (ProgressType(progressType_->Get())) {
490 case ProgressType::LINEAR:
491 return linearSweepEffect_;
492 case ProgressType::CAPSULE:
493 return sweepEffect_;
494 case ProgressType::RING:
495 return ringSweepEffect_;
496 default:
497 return false;
498 }
499 }
500
StopSweepingAnimation(float date)501 void ProgressModifier::StopSweepingAnimation(float date)
502 {
503 if (isSweeping_) {
504 isSweeping_ = false;
505 dateUpdated_ = false;
506 AnimationOption option = AnimationOption();
507 option.SetDuration(0);
508 AnimationUtils::Animate(option, [&]() { sweepingDate_->Set(date); });
509 }
510 }
511
CalcRingProgressAdditionalAngle() const512 float ProgressModifier::CalcRingProgressAdditionalAngle() const
513 {
514 auto contentSize = contentSize_->Get();
515 auto strokeWidth = strokeWidth_->Get();
516 PointF centerPt = PointF(contentSize.Width() / 2, contentSize.Height() / 2);
517 float radius = std::min(contentSize.Width() / 2, contentSize.Height() / 2);
518 auto paintShadow = paintShadow_->Get() && GreatNotEqual(radius, RING_SHADOW_VALID_RADIUS_MIN);
519 auto shadowBlurOffset = paintShadow ? strokeWidth / 2 + std::max(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y) : 0.0f;
520 if (GreatOrEqual(strokeWidth + shadowBlurOffset, radius)) {
521 strokeWidth = radius / 2;
522 shadowBlurOffset = paintShadow ? strokeWidth / 2 + std::max(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y) : 0.0f;
523 }
524 return asinf((strokeWidth / 2) / (radius - strokeWidth / 2 - shadowBlurOffset)) * ANGLE_180 / PI_NUM;
525 }
526
StartLinearSweepingAnimation(float value)527 void ProgressModifier::StartLinearSweepingAnimation(float value)
528 {
529 auto contentSize = contentSize_->Get();
530 float radius = strokeWidth_->Get() / 2;
531 float barLength = 0.0f;
532 float dateLength = 0.0f;
533
534 if (GreatOrEqual(contentSize.Width(), contentSize.Height())) {
535 barLength = contentSize.Width() - radius * 2;
536 } else {
537 barLength = contentSize.Height() - radius * 2;
538 }
539
540 if (NearEqual(barLength, 0.0f)) {
541 return;
542 }
543
544 dateLength = barLength * value / maxValue_->Get();
545 if (NearEqual(dateLength, 0.0f)) {
546 return;
547 }
548
549 float date = dateLength + strokeWidth_->Get() + LINEAR_SWEEPING_LEN.ConvertToPx();
550 float sweepSpeed = barLength / TIME_2000; // It takes 2 seconds to sweep the whole bar length
551 if (!isSweeping_ && linearSweepEffect_->Get()) {
552 StartLinearSweepingAnimationImpl(date, sweepSpeed);
553 } else if (!linearSweepEffect_->Get() || NearEqual(valueBackup_, maxValue_->Get())) {
554 StopSweepingAnimation();
555 } else {
556 dateUpdated_ = !NearEqual(sweepingDateBackup_, date);
557 }
558
559 sweepingDateBackup_ = date;
560 }
561
StartLinearSweepingAnimationImpl(float date,float speed)562 void ProgressModifier::StartLinearSweepingAnimationImpl(float date, float speed)
563 {
564 if (!isVisible_ || !linearSweepEffect_) {
565 return;
566 }
567
568 if (NearEqual(valueBackup_, maxValue_->Get())) {
569 return;
570 }
571
572 auto context = PipelineBase::GetCurrentContext();
573 CHECK_NULL_VOID(context);
574 bool isFormRender = context->IsFormRender();
575 isSweeping_ = true;
576 sweepingDate_->Set(0.0f);
577 speed = NearZero(speed) ? 1.0f : speed;
578 int32_t time = date / speed;
579 AnimationOption option = AnimationOption();
580 auto motion = AceType::MakeRefPtr<LinearCurve>();
581 option.SetCurve(motion);
582 option.SetIteration(isFormRender ? 1 : -1);
583 option.SetDuration(time);
584 AnimationUtils::Animate(
585 option,
586 [&]() { sweepingDate_->Set(date); },
587 nullptr,
588 [weak = WeakClaim(this), id = Container::CurrentId(), speed]() {
589 ContainerScope scope(id);
590 auto modifier = weak.Upgrade();
591 CHECK_NULL_VOID(modifier);
592 if (modifier->dateUpdated_) {
593 modifier->dateUpdated_ = false;
594 float currentDate = modifier->sweepingDate_->Get();
595 modifier->StopSweepingAnimation(currentDate);
596 modifier->StartContinuousSweepingAnimation(currentDate, modifier->sweepingDateBackup_, speed);
597 auto context = PipelineBase::GetCurrentContext();
598 context->RequestFrame();
599 }
600 });
601 }
602
SetMaxValue(float value)603 void ProgressModifier::SetMaxValue(float value)
604 {
605 CHECK_NULL_VOID(maxValue_);
606 maxValue_->Set(value);
607 }
608
SetValue(float value)609 void ProgressModifier::SetValue(float value)
610 {
611 valueBackup_ = value;
612
613 if (isLoading_) {
614 return;
615 }
616
617 CHECK_NULL_VOID(value_);
618 AnimationOption option = AnimationOption();
619 if (smoothEffect_->Get()) {
620 if (isVisible_) {
621 auto motion =
622 AceType::MakeRefPtr<ResponsiveSpringMotion>(SPRING_MOTION_RESPONSE, SPRING_MOTION_DAMPING_FRACTION);
623 option.SetCurve(motion);
624 } else {
625 option.SetDuration(0);
626 }
627 AnimationUtils::Animate(option, [&]() { value_->Set(value); });
628 } else {
629 value_->Set(value);
630 }
631 ProcessSweepingAnimation(ProgressType(progressType_->Get()), value);
632 }
633
SetScaleWidth(float value)634 void ProgressModifier::SetScaleWidth(float value)
635 {
636 CHECK_NULL_VOID(scaleWidth_);
637 scaleWidth_->Set(value);
638 }
639
SetScaleCount(int32_t value)640 void ProgressModifier::SetScaleCount(int32_t value)
641 {
642 CHECK_NULL_VOID(scaleCount_);
643 scaleCount_->Set(value);
644 }
645
SetContentOffset(const OffsetF & offset)646 void ProgressModifier::SetContentOffset(const OffsetF& offset)
647 {
648 CHECK_NULL_VOID(offset_);
649 offset_->Set(offset);
650 }
651
SetContentSize(const SizeF & contentSize)652 void ProgressModifier::SetContentSize(const SizeF& contentSize)
653 {
654 CHECK_NULL_VOID(contentSize_);
655 contentSize_->Set(contentSize);
656 }
657
SetBorderWidth(float width)658 void ProgressModifier::SetBorderWidth(float width)
659 {
660 capsuleBorderWidth_->Set(width);
661 }
662
SetSweepEffect(bool value)663 void ProgressModifier::SetSweepEffect(bool value)
664 {
665 sweepEffect_->Set(value);
666 }
667
SetRingSweepEffect(bool value)668 void ProgressModifier::SetRingSweepEffect(bool value)
669 {
670 ringSweepEffect_->Set(value);
671 }
672
SetLinearSweepEffect(bool value)673 void ProgressModifier::SetLinearSweepEffect(bool value)
674 {
675 linearSweepEffect_->Set(value);
676 }
677
ContentDrawWithFunction(DrawingContext & context)678 void ProgressModifier::ContentDrawWithFunction(DrawingContext& context)
679 {
680 auto contentSize = contentSize_->Get();
681 auto& canvas = context.canvas;
682 if (progressType_->Get() == static_cast<int32_t>(ProgressType::LINEAR)) {
683 PaintLinear(canvas, offset_->Get(), contentSize);
684 } else if (progressType_->Get() == static_cast<int32_t>(ProgressType::RING)) {
685 PaintRing(canvas, offset_->Get(), contentSize);
686 } else if (progressType_->Get() == static_cast<int32_t>(ProgressType::SCALE)) {
687 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
688 PaintScaleRingForApiNine(canvas, offset_->Get(), contentSize);
689 } else {
690 PaintScaleRing(canvas, offset_->Get(), contentSize);
691 }
692 } else if (progressType_->Get() == static_cast<int32_t>(ProgressType::MOON)) {
693 PaintMoon(canvas, offset_->Get(), contentSize);
694 } else if (progressType_->Get() == static_cast<int32_t>(ProgressType::CAPSULE)) {
695 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
696 if (contentSize.Width() >= contentSize.Height()) {
697 PaintCapsuleForApiNine(canvas, offset_->Get(), contentSize);
698 } else {
699 PaintVerticalCapsuleForApiNine(canvas, offset_->Get(), contentSize);
700 }
701 } else {
702 if (contentSize.Width() >= contentSize.Height()) {
703 PaintCapsule(canvas, offset_->Get(), contentSize);
704 } else {
705 PaintVerticalCapsule(canvas, offset_->Get(), contentSize);
706 }
707 }
708 } else {
709 PaintLinear(canvas, offset_->Get(), contentSize);
710 }
711 }
712
PaintLinear(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const713 void ProgressModifier::PaintLinear(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
714 {
715 RSBrush brush;
716 brush.SetAntiAlias(true);
717 brush.SetColor(ToRSColor(bgColor_->Get()));
718 double radius = strokeRadius_->Get();
719 if (contentSize.Width() >= contentSize.Height()) {
720 double barLength = contentSize.Width() - radius * INT32_TWO;
721 CHECK_NULL_VOID(Positive(barLength));
722 double dateLength = std::min(barLength * value_->Get() / maxValue_->Get(), barLength);
723 canvas.AttachBrush(brush);
724 canvas.DrawRoundRect(
725 { { offset.GetX(), offset.GetY(), contentSize.Width() + offset.GetX(),
726 strokeWidth_->Get() + offset.GetY() },
727 radius, radius });
728 canvas.DetachBrush();
729 // progress selected part
730 CHECK_NULL_VOID(Positive(dateLength));
731 brush.SetColor(ToRSColor((color_->Get())));
732 canvas.AttachBrush(brush);
733 #ifndef USE_ROSEN_DRAWING
734 RSPath path;
735 #else
736 RSRecordingPath path;
737 #endif
738 float leftTopX = !isRightToLeft_->Get()
739 ? offset.GetX()
740 : contentSize.Width() + offset.GetX() - (dateLength + radius * INT32_TWO);
741 float leftTopY = offset.GetY();
742 float rightBottomX = !isRightToLeft_->Get() ? dateLength + offset.GetX() + radius * INT32_TWO
743 : contentSize.Width() + offset.GetX();
744 float rightBottomY = strokeWidth_->Get() + offset.GetY();
745 path.AddRoundRect({ leftTopX, leftTopY, rightBottomX, rightBottomY }, radius, radius);
746 canvas.DrawPath(path);
747 canvas.DetachBrush();
748 canvas.Restore();
749 PaintLinearSweeping(canvas, offset, path, true, contentSize);
750 } else {
751 double barLength = contentSize.Height() - radius * INT32_TWO;
752 CHECK_NULL_VOID(Positive(barLength));
753 double dateLength = std::min(barLength * value_->Get() / maxValue_->Get(), barLength);
754 canvas.AttachBrush(brush);
755 canvas.DrawRoundRect(
756 { { offset.GetX(), offset.GetY(), strokeWidth_->Get() + offset.GetX(),
757 contentSize.Height() + offset.GetY() },
758 radius, radius });
759 canvas.DetachBrush();
760 // progress selected part
761 CHECK_NULL_VOID(Positive(dateLength));
762 brush.SetColor(ToRSColor((color_->Get())));
763 canvas.AttachBrush(brush);
764 #ifndef USE_ROSEN_DRAWING
765 RSPath path;
766 #else
767 RSRecordingPath path;
768 #endif
769 path.AddRoundRect(
770 { offset.GetX(), offset.GetY(), strokeWidth_->Get() + offset.GetX(),
771 dateLength + offset.GetY() + radius * INT32_TWO },
772 radius, radius);
773 canvas.DrawPath(path);
774 canvas.DetachBrush();
775 canvas.Restore();
776 PaintLinearSweeping(canvas, offset, path, false, contentSize);
777 }
778 }
779
PaintLinearSweeping(RSCanvas & canvas,const OffsetF & offset,const RSPath & path,bool isHorizontal,const SizeF & contentSize) const780 void ProgressModifier::PaintLinearSweeping(
781 RSCanvas& canvas, const OffsetF& offset, const RSPath& path, bool isHorizontal, const SizeF& contentSize) const
782 {
783 if (NearZero(value_->Get()) || NearZero(maxValue_->Get())) {
784 return;
785 }
786
787 // If the progress reaches 100%, stop sweeping.
788 if (NearEqual(value_->Get(), maxValue_->Get())) {
789 return;
790 }
791
792 std::vector<RSColorQuad> colors;
793 std::vector<float> pos;
794 GenerateLinearSweepingGradientInfo(colors, pos);
795
796 RSBrush brush;
797 float leftTopX = 0.0f;
798 float leftTopY = 0.0f;
799 float rightBottomX = 0.0f;
800 float rightBottomY = 0.0f;
801 if (isHorizontal) {
802 leftTopX = !isRightToLeft_->Get() ? offset.GetX() + sweepingDate_->Get() - LINEAR_SWEEPING_LEN.ConvertToPx()
803 : contentSize.Width() + offset.GetX() - sweepingDate_->Get();
804 leftTopY = offset.GetY();
805 rightBottomX = !isRightToLeft_->Get() ? offset.GetX() + sweepingDate_->Get()
806 : contentSize.Width() + offset.GetX() - sweepingDate_->Get() +
807 LINEAR_SWEEPING_LEN.ConvertToPx();
808 rightBottomY = offset.GetY() + strokeWidth_->Get();
809 } else {
810 leftTopX = offset.GetX();
811 leftTopY = offset.GetY() + sweepingDate_->Get() - LINEAR_SWEEPING_LEN.ConvertToPx();
812 rightBottomX = offset.GetX() + strokeWidth_->Get();
813 rightBottomY = offset.GetY() + sweepingDate_->Get();
814 }
815
816 RSRect rect(leftTopX, leftTopY, rightBottomX, rightBottomY);
817 #ifndef USE_ROSEN_DRAWING
818 brush.SetShaderEffect(RSShaderEffect::CreateLinearGradient(
819 ToRSPoint(PointF(leftTopX, leftTopY)),
820 ToRSPoint(PointF(rightBottomX, rightBottomY)),
821 colors, pos, RSTileMode::CLAMP));
822 #else
823 brush.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
824 ToRSPoint(PointF(leftTopX, leftTopY)),
825 ToRSPoint(PointF(rightBottomX, rightBottomY)),
826 colors, pos, RSTileMode::CLAMP));
827 #endif
828 canvas.Save();
829 canvas.ClipPath(path, RSClipOp::INTERSECT, true);
830 canvas.AttachBrush(brush);
831 canvas.DrawRect(rect);
832 canvas.DetachBrush();
833 canvas.Restore();
834 }
835
GenerateLinearSweepingGradientInfo(std::vector<RSColorQuad> & colors,std::vector<float> & pos) const836 void ProgressModifier::GenerateLinearSweepingGradientInfo(
837 std::vector<RSColorQuad>& colors, std::vector<float>& pos) const
838 {
839 Gradient gradient;
840 Color sweepingColorBase = Color::WHITE;
841 Color sweepingColorEnd = sweepingColorBase.ChangeOpacity(0.0);
842 GradientColor gradientColorEnd;
843 gradientColorEnd.SetColor(sweepingColorEnd);
844 gradientColorEnd.SetDimension(Dimension(0.0, DimensionUnit::PERCENT));
845 gradient.AddColor(gradientColorEnd);
846
847 // The sweep layer is a white gradient layer of length 80 vp with a opacity of 0.3 at 75 vp and 0.0 at both ends.
848 Dimension stageLen = 75.0_vp;
849 Color sweepingColorMiddle = sweepingColorBase.ChangeOpacity(0.3);
850 GradientColor gradientColorMiddle;
851 gradientColorMiddle.SetColor(sweepingColorMiddle);
852 gradientColorMiddle.SetDimension(
853 Dimension(!isRightToLeft_->Get() ? stageLen.ConvertToPx() / LINEAR_SWEEPING_LEN.ConvertToPx()
854 : 1 - stageLen.ConvertToPx() / LINEAR_SWEEPING_LEN.ConvertToPx(),
855 DimensionUnit::PERCENT));
856 gradient.AddColor(gradientColorMiddle);
857
858 Color sweepingColorStart = sweepingColorBase.ChangeOpacity(0.0);
859 GradientColor gradientColorStart;
860 gradientColorStart.SetColor(sweepingColorStart);
861 gradientColorStart.SetDimension(Dimension(1.0, DimensionUnit::PERCENT));
862 gradient.AddColor(gradientColorStart);
863
864 auto gradientColors = gradient.GetColors();
865 for (size_t i = 0; i < gradientColors.size(); i++) {
866 colors.emplace_back(gradientColors[i].GetColor().GetValue());
867 pos.emplace_back(gradientColors[i].GetDimension().Value());
868 }
869 }
870
PaintRing(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const871 void ProgressModifier::PaintRing(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
872 {
873 auto centerPt = PointF(contentSize.Width() / 2, contentSize.Height() / 2) + offset;
874 auto radius = std::min(contentSize.Width() / 2, contentSize.Height() / 2);
875 auto angle = std::min((value_->Get() / maxValue_->Get()) * ANGLE_360, static_cast<float>(ANGLE_360));
876 auto thickness = strokeWidth_->Get();
877 // No shadow is drawn if radius is less or equal to 10, because it is no enough space to draw both ring and shadow.
878 auto paintShadow = paintShadow_->Get() && GreatNotEqual(radius, RING_SHADOW_VALID_RADIUS_MIN);
879 auto shadowBlurOffset = paintShadow ? thickness / 2 + std::max(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y) : 0.0f;
880 if (GreatOrEqual(thickness + shadowBlurOffset, radius)) {
881 thickness = radius / 2;
882 shadowBlurOffset = paintShadow ? thickness / 2 + std::max(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y) : 0.0f;
883 }
884 // The shadowBlurSigma is an empirical value. If it is greater than thickness / 5, the shadow will be cut by
885 // the canvas boundary.
886 auto shadowBlurSigma = std::max(thickness / 5, RING_SHADOW_BLUR_RADIUS_MIN);
887 radius = radius - thickness / 2 - shadowBlurOffset;
888
889 RingProgressData ringData;
890 ringData.centerPt = centerPt;
891 ringData.radius = radius;
892 ringData.angle = angle;
893 ringData.thickness = thickness;
894 ringData.shadowBlurSigma = shadowBlurSigma;
895
896 PaintRingBackground(canvas, ringData);
897 if (isRightToLeft_->Get()) {
898 canvas.Scale(-1, 1);
899 canvas.Translate(-((radius + shadowBlurOffset) * INT32_TWO + thickness), 0);
900 }
901 if (isLoading_) {
902 PaintTrailing(canvas, ringData);
903 return;
904 }
905
906 if (NearZero(angle)) {
907 return;
908 }
909
910 if (paintShadow) {
911 ringData.centerPt = centerPt + OffsetF(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y);
912 PaintRingProgressOrShadow(canvas, ringData, true);
913 }
914 ringData.centerPt = centerPt;
915 PaintRingProgressOrShadow(canvas, ringData, false);
916 PaintRingSweeping(canvas, ringData);
917 }
918
PaintRingBackground(RSCanvas & canvas,const RingProgressData & ringProgressData) const919 void ProgressModifier::PaintRingBackground(RSCanvas& canvas, const RingProgressData& ringProgressData) const
920 {
921 RSPen pen;
922 pen.SetAntiAlias(true);
923 pen.SetWidth(ringProgressData.thickness);
924 pen.SetCapStyle(ToRSCapStyle(LineCap::ROUND));
925 pen.SetColor(ToRSColor(bgColor_->Get()));
926
927 canvas.Save();
928 canvas.AttachPen(pen);
929 canvas.DrawCircle(ToRSPoint(ringProgressData.centerPt), ringProgressData.radius);
930 canvas.DetachPen();
931 canvas.Restore();
932 }
933
GetRingProgressGradientColors() const934 std::vector<GradientColor> ProgressModifier::GetRingProgressGradientColors() const
935 {
936 Gradient gradient = SortGradientColorsByOffset(ringProgressColors_->Get().GetGradient());
937 std::vector<GradientColor> gradientColors = gradient.GetColors();
938 // Fault protection processing, if gradientColors is empty, set to default colors.
939
940 if (gradientColors.empty()) {
941 auto pipeline = PipelineBase::GetCurrentContext();
942 CHECK_NULL_RETURN(pipeline, gradientColors);
943 auto theme = pipeline->GetTheme<ProgressTheme>();
944 CHECK_NULL_RETURN(theme, gradientColors);
945 GradientColor endColor;
946 GradientColor beginColor;
947 endColor.SetLinearColor(LinearColor(theme->GetRingProgressEndSideColor()));
948 endColor.SetDimension(0.0);
949 beginColor.SetLinearColor(LinearColor(theme->GetRingProgressBeginSideColor()));
950 beginColor.SetDimension(1.0);
951 gradientColors.emplace_back(endColor);
952 gradientColors.emplace_back(beginColor);
953 }
954 return gradientColors;
955 }
956
PaintRingProgressOrShadow(RSCanvas & canvas,const RingProgressData & ringProgressData,bool isShadow) const957 void ProgressModifier::PaintRingProgressOrShadow(
958 RSCanvas& canvas, const RingProgressData& ringProgressData, bool isShadow) const
959 {
960 auto angle = ringProgressData.angle;
961 float additionalAngle = CalcRingProgressAdditionalAngle();
962
963 std::vector<GradientColor> gradientColors = GetRingProgressGradientColors();
964
965 RSBrush brush;
966 brush.SetAntiAlias(true);
967
968 if (isShadow) {
969 for (size_t i = 0; i < gradientColors.size(); i++) {
970 Color color = gradientColors[i].GetLinearColor().BlendOpacity(RING_SHADOW_OPACITY);
971 gradientColors[i].SetLinearColor(LinearColor(color));
972 }
973 RSFilter filter;
974 #ifndef USE_ROSEN_DRAWING
975 filter.SetImageFilter(RSImageFilter::CreateBlurImageFilter(
976 ringProgressData.shadowBlurSigma, ringProgressData.shadowBlurSigma, RSTileMode::DECAL, nullptr));
977 #else
978 filter.SetImageFilter(RSRecordingImageFilter::CreateBlurImageFilter(
979 ringProgressData.shadowBlurSigma, ringProgressData.shadowBlurSigma, RSTileMode::DECAL, nullptr));
980 #endif
981 brush.SetFilter(filter);
982 }
983
984 if (LessOrEqual(angle + additionalAngle * INT32_TWO, ANGLE_360)) {
985 PaintWhole(canvas, brush, ringProgressData, gradientColors);
986 } else {
987 PaintBeginHalf(canvas, brush, ringProgressData, gradientColors);
988 PaintEndHalf(canvas, brush, ringProgressData, gradientColors);
989 }
990 }
991
PaintWhole(RSCanvas & canvas,RSBrush & brush,const RingProgressData & ringProgressData,std::vector<GradientColor> & gradientColors) const992 void ProgressModifier::PaintWhole(RSCanvas& canvas, RSBrush& brush, const RingProgressData& ringProgressData,
993 std::vector<GradientColor>& gradientColors) const
994 {
995 PointF centerPt = ringProgressData.centerPt;
996 auto radius = ringProgressData.radius;
997 auto angle = ringProgressData.angle;
998 auto thickness = ringProgressData.thickness;
999 double halfThickness = thickness / 2;
1000 float additionalAngle = CalcRingProgressAdditionalAngle();
1001 double radAngle = angle / ANGLE_180 * PI_NUM;
1002 double radAdditionalAngle = additionalAngle / ANGLE_180 * PI_NUM;
1003 double sinPointOutside = std::sin(radAngle + radAdditionalAngle) * (radius - halfThickness);
1004 double cosPointOutside = std::cos(radAngle + radAdditionalAngle) * (radius - halfThickness);
1005 double sinPointStart = std::sin(radAdditionalAngle) * radius;
1006 double cosPointStart = std::cos(radAdditionalAngle) * radius;
1007
1008 std::vector<RSColorQuad> colors;
1009 std::vector<float> pos;
1010 for (size_t i = 0; i < gradientColors.size(); i++) {
1011 colors.emplace_back(gradientColors[i].GetLinearColor().GetValue());
1012 pos.emplace_back(gradientColors[i].GetDimension().Value());
1013 }
1014
1015 RSPath path;
1016 #ifndef USE_ROSEN_DRAWING
1017 brush.SetShaderEffect(RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())),
1018 colors, pos, RSTileMode::CLAMP, 0, angle + additionalAngle * INT32_TWO, nullptr));
1019 #else
1020 brush.SetShaderEffect(
1021 RSRecordingShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos,
1022 RSTileMode::CLAMP, 0, angle + additionalAngle * INT32_TWO, nullptr));
1023 #endif
1024 path.ArcTo(centerPt.GetX() + cosPointStart - halfThickness, centerPt.GetY() + sinPointStart - halfThickness,
1025 centerPt.GetX() + cosPointStart + halfThickness, centerPt.GetY() + sinPointStart + halfThickness,
1026 ANGLE_180 + additionalAngle, ANGLE_180);
1027 path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1028 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness, additionalAngle, angle);
1029 path.ArcTo(halfThickness, halfThickness, 0.0f, RSPathDirection::CW_DIRECTION, centerPt.GetX() + cosPointOutside,
1030 centerPt.GetY() + sinPointOutside);
1031 path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1032 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness, additionalAngle + angle,
1033 -angle);
1034 path.Close();
1035 canvas.Save();
1036 canvas.Rotate(-ANGLE_90 - additionalAngle, centerPt.GetX(), centerPt.GetY());
1037 canvas.AttachBrush(brush);
1038 canvas.DrawPath(path);
1039 canvas.DetachBrush();
1040 canvas.Restore();
1041 }
1042
PaintBeginHalf(RSCanvas & canvas,RSBrush & brush,const RingProgressData & ringProgressData,std::vector<GradientColor> & gradientColors) const1043 void ProgressModifier::PaintBeginHalf(RSCanvas& canvas, RSBrush& brush, const RingProgressData& ringProgressData,
1044 std::vector<GradientColor>& gradientColors) const
1045 {
1046 PointF centerPt = ringProgressData.centerPt;
1047 auto radius = ringProgressData.radius;
1048 auto angle = ringProgressData.angle;
1049 auto thickness = ringProgressData.thickness;
1050 double halfThickness = thickness / 2;
1051 float additionalAngle = CalcRingProgressAdditionalAngle();
1052
1053 std::vector<RSColorQuad> colors;
1054 std::vector<float> pos;
1055 for (size_t i = 0; i < gradientColors.size(); i++) {
1056 colors.emplace_back(gradientColors[i].GetLinearColor().GetValue());
1057 pos.emplace_back(gradientColors[i].GetDimension().Value());
1058 }
1059 colors.emplace_back(gradientColors[0].GetLinearColor().GetValue());
1060 pos.emplace_back((ANGLE_360 - additionalAngle) / ANGLE_360);
1061
1062 RSPath beginPath;
1063 RSPath path;
1064 #ifndef USE_ROSEN_DRAWING
1065 brush.SetShaderEffect(RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())),
1066 colors, pos, RSTileMode::CLAMP, 0, angle, nullptr));
1067 #else
1068 brush.SetShaderEffect(RSRecordingShaderEffect::CreateSweepGradient(
1069 ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos, RSTileMode::CLAMP, 0, angle, nullptr));
1070 #endif
1071 beginPath.MoveTo(centerPt.GetX() + radius - halfThickness, centerPt.GetY());
1072 beginPath.ArcTo(centerPt.GetX() + radius - halfThickness, centerPt.GetY() - halfThickness,
1073 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + halfThickness, ANGLE_180, ANGLE_180);
1074 beginPath.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1075 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness, 0, ANGLE_180);
1076 beginPath.LineTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY());
1077 beginPath.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1078 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness, ANGLE_180, -ANGLE_180);
1079 beginPath.Close();
1080
1081 canvas.Save();
1082 canvas.Rotate(-ANGLE_90, centerPt.GetX(), centerPt.GetY());
1083 canvas.AttachBrush(brush);
1084 canvas.DrawPath(beginPath);
1085 canvas.DetachBrush();
1086 canvas.Restore();
1087 }
1088
PaintEndHalf(RSCanvas & canvas,RSBrush & brush,const RingProgressData & ringProgressData,std::vector<GradientColor> & gradientColors) const1089 void ProgressModifier::PaintEndHalf(RSCanvas& canvas, RSBrush& brush, const RingProgressData& ringProgressData,
1090 std::vector<GradientColor>& gradientColors) const
1091 {
1092 PointF centerPt = ringProgressData.centerPt;
1093 auto radius = ringProgressData.radius;
1094 auto angle = ringProgressData.angle;
1095 auto thickness = ringProgressData.thickness;
1096 double halfThickness = thickness / 2;
1097 float additionalAngle = CalcRingProgressAdditionalAngle();
1098 double radArc = (angle - ANGLE_180) / ANGLE_180 * PI_NUM;
1099 double sinPointOutside = std::sin(radArc) * (radius - halfThickness);
1100 double cosPointOutside = std::cos(radArc) * (radius - halfThickness);
1101
1102 std::vector<RSColorQuad> colors;
1103 std::vector<float> pos;
1104 if (gradientColors.size() != 0) {
1105 colors.emplace_back(gradientColors[gradientColors.size() - 1].GetLinearColor().GetValue());
1106 }
1107 pos.emplace_back(additionalAngle / ANGLE_360);
1108 for (size_t i = 0; i < gradientColors.size(); i++) {
1109 colors.emplace_back(gradientColors[i].GetLinearColor().GetValue());
1110 pos.emplace_back(gradientColors[i].GetDimension().Value());
1111 }
1112
1113 RSPath endPath;
1114 #ifndef USE_ROSEN_DRAWING
1115 brush.SetShaderEffect(RSShaderEffect::CreateSweepGradient(
1116 ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos, RSTileMode::CLAMP, 0, angle, nullptr));
1117 #else
1118 brush.SetShaderEffect(RSRecordingShaderEffect::CreateSweepGradient(
1119 ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos, RSTileMode::CLAMP, 0, angle, nullptr));
1120 #endif
1121 endPath.MoveTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY());
1122 endPath.LineTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY());
1123 endPath.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1124 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness, ANGLE_180,
1125 angle - ANGLE_180);
1126 endPath.ArcTo(halfThickness, halfThickness, 0.0f, RSPathDirection::CW_DIRECTION, centerPt.GetX() - cosPointOutside,
1127 centerPt.GetY() - sinPointOutside);
1128 endPath.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1129 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness, angle, ANGLE_180 - angle);
1130 endPath.Close();
1131 canvas.Save();
1132 canvas.Rotate(-ANGLE_90, centerPt.GetX(), centerPt.GetY());
1133 canvas.AttachBrush(brush);
1134 canvas.DrawPath(endPath);
1135 canvas.DetachBrush();
1136 canvas.Restore();
1137 }
1138
SortGradientColorsByOffset(const Gradient & gradient) const1139 Gradient ProgressModifier::SortGradientColorsByOffset(const Gradient& gradient) const
1140 {
1141 auto srcGradientColors = gradient.GetColors();
1142 std::sort(
1143 srcGradientColors.begin(), srcGradientColors.end(), [](const GradientColor& left, const GradientColor& right) {
1144 return left.GetDimension().Value() < right.GetDimension().Value();
1145 });
1146
1147 Gradient sortedGradient;
1148 for (const auto& item : srcGradientColors) {
1149 sortedGradient.AddColor(item);
1150 }
1151
1152 return sortedGradient;
1153 }
1154
PaintRingSweeping(RSCanvas & canvas,const RingProgressData & ringProgressData) const1155 void ProgressModifier::PaintRingSweeping(RSCanvas& canvas, const RingProgressData& ringProgressData) const
1156 {
1157 if (NearZero(value_->Get()) || NearZero(maxValue_->Get())) {
1158 return;
1159 }
1160
1161 // If the progress reaches 100%, stop sweeping.
1162 if (NearEqual(value_->Get(), maxValue_->Get())) {
1163 return;
1164 }
1165
1166 auto centerPt = ringProgressData.centerPt;
1167 auto radius = ringProgressData.radius;
1168 auto thickness = ringProgressData.thickness;
1169 double halfThickness = thickness / 2;
1170
1171 RSPen pen;
1172 pen.SetAntiAlias(true);
1173 pen.SetWidth(thickness);
1174 pen.SetCapStyle(ToRSCapStyle(LineCap::ROUND));
1175
1176 std::vector<RSColorQuad> colors;
1177 std::vector<float> pos;
1178 GenerateRingSweepingGradientInfo(colors, pos);
1179
1180 float date = sweepingDate_->Get();
1181 float additionalAngle = CalcRingProgressAdditionalAngle();
1182 float startSweepAngle = date - ANGLE_45 - additionalAngle;
1183 #ifndef USE_ROSEN_DRAWING
1184 pen.SetShaderEffect(RSShaderEffect::CreateSweepGradient(
1185 ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos,
1186 RSTileMode::CLAMP, 0, ANGLE_45, nullptr));
1187
1188 RSPath path;
1189 #else
1190 pen.SetShaderEffect(RSRecordingShaderEffect::CreateSweepGradient(
1191 ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos,
1192 RSTileMode::CLAMP, 0, ANGLE_45, nullptr));
1193
1194 RSRecordingPath path;
1195 #endif
1196 double totalDegree = ANGLE_360;
1197 double angleArc = (value_->Get() / maxValue_->Get()) * totalDegree;
1198 double radArc = angleArc / totalDegree * 2 * PI_NUM;
1199 double sinPointInside = std::sin(radArc) * (radius - halfThickness);
1200 double cosPointInside = std::cos(radArc) * (radius - halfThickness);
1201 if (LessOrEqual(startSweepAngle, ANGLE_90)) {
1202 path.MoveTo(centerPt.GetX(), centerPt.GetY() - radius + halfThickness);
1203 path.ArcTo(centerPt.GetX() - halfThickness, centerPt.GetY() - radius - halfThickness,
1204 centerPt.GetX() + halfThickness, centerPt.GetY() - radius + halfThickness,
1205 ANGLE_90, ANGLE_180);
1206 if (GreatNotEqual(angleArc + additionalAngle * INT32_TWO, ANGLE_360)) {
1207 path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1208 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness,
1209 -ANGLE_90, ANGLE_180);
1210 path.LineTo(centerPt.GetX(), centerPt.GetY() + radius - halfThickness);
1211 path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1212 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness,
1213 ANGLE_90, -ANGLE_180);
1214 #ifndef USE_ROSEN_DRAWING
1215 RSPath path2;
1216 RSPath path3;
1217 #else
1218 RSRecordingPath path2;
1219 RSRecordingPath path3;
1220 #endif
1221 float x = centerPt.GetX() + sin(radArc) * radius;
1222 float y = centerPt.GetY() - cos(radArc) * radius;
1223 path2.AddCircle(x, y, halfThickness);
1224 path3.Op(path, path2, RSPathOp::INTERSECT);
1225 path.Op(path, path3, RSPathOp::DIFFERENCE);
1226 } else if (GreatNotEqual(angleArc, ANGLE_180)) {
1227 path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1228 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness,
1229 -ANGLE_90, ANGLE_180);
1230 path.LineTo(centerPt.GetX(), centerPt.GetY() + radius - halfThickness);
1231 path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1232 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness,
1233 ANGLE_90, -ANGLE_180);
1234 } else {
1235 path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1236 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness,
1237 -ANGLE_90, angleArc);
1238 path.ArcTo(halfThickness, halfThickness, 0.0f, RSPathDirection::CW_DIRECTION,
1239 centerPt.GetX() + sinPointInside, centerPt.GetY() - cosPointInside);
1240 path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1241 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness,
1242 -ANGLE_90 + angleArc, -angleArc);
1243 }
1244 } else {
1245 path.MoveTo(centerPt.GetX() + radius - halfThickness, centerPt.GetY());
1246 path.LineTo(centerPt.GetX() + radius + halfThickness, centerPt.GetY());
1247 path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1248 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness,
1249 0, angleArc - ANGLE_90);
1250 path.ArcTo(halfThickness, halfThickness, 0.0f, RSPathDirection::CW_DIRECTION,
1251 centerPt.GetX() + sinPointInside, centerPt.GetY() - cosPointInside);
1252 path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1253 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness,
1254 angleArc - ANGLE_90, -angleArc + ANGLE_90);
1255 }
1256
1257 canvas.Save();
1258 canvas.ClipPath(path, RSClipOp::INTERSECT, true);
1259
1260 float rotateAngle = -ANGLE_90 + startSweepAngle;
1261 canvas.Rotate(rotateAngle, centerPt.GetX(), centerPt.GetY());
1262 canvas.AttachPen(pen);
1263 canvas.DrawArc(
1264 { centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1265 0, ANGLE_45);
1266 canvas.DetachPen();
1267 canvas.Restore();
1268 }
1269
GenerateRingSweepingGradientInfo(std::vector<RSColorQuad> & colors,std::vector<float> & pos) const1270 void ProgressModifier::GenerateRingSweepingGradientInfo(
1271 std::vector<RSColorQuad>& colors, std::vector<float>& pos) const
1272 {
1273 Gradient gradient;
1274 Color sweepingColorBase = Color::WHITE;
1275 Color sweepingColorEnd = sweepingColorBase.ChangeOpacity(0.0f);
1276 GradientColor gradientColorEnd;
1277 gradientColorEnd.SetColor(sweepingColorEnd);
1278 gradientColorEnd.SetDimension(Dimension(0.0, DimensionUnit::PERCENT));
1279 gradient.AddColor(gradientColorEnd);
1280
1281 // The sweep layer is a 45-degree white gradient with an opacity of 0.4 at 35 degrees and 0 at both ends.
1282 Color sweepingColorMiddle = sweepingColorBase.ChangeOpacity(0.4f);
1283 GradientColor gradientColorMiddle;
1284 gradientColorMiddle.SetColor(sweepingColorMiddle);
1285 gradientColorMiddle.SetDimension(Dimension(35.0f / ANGLE_45, DimensionUnit::PERCENT));
1286 gradient.AddColor(gradientColorMiddle);
1287
1288 Color sweepingColorStart = sweepingColorBase.ChangeOpacity(0.0f);
1289 GradientColor gradientColorStart;
1290 gradientColorStart.SetColor(sweepingColorStart);
1291 gradientColorStart.SetDimension(Dimension(1.0, DimensionUnit::PERCENT));
1292 gradient.AddColor(gradientColorStart);
1293
1294 auto gradientColors = gradient.GetColors();
1295 for (size_t i = 0; i < gradientColors.size(); i++) {
1296 colors.emplace_back(gradientColors[i].GetColor().GetValue());
1297 pos.emplace_back(gradientColors[i].GetDimension().Value());
1298 }
1299 }
1300
PaintTrailing(RSCanvas & canvas,const RingProgressData & ringProgressData) const1301 void ProgressModifier::PaintTrailing(RSCanvas& canvas, const RingProgressData& ringProgressData) const
1302 {
1303 PointF centerPt = ringProgressData.centerPt;
1304 double radius = ringProgressData.radius;
1305 double thickness = ringProgressData.thickness;
1306 float head = trailingHeadDate_->Get();
1307 float tail = trailingTailDate_->Get();
1308 float additionalAngle = CalcRingProgressAdditionalAngle();
1309 float rotateAngle = 0.0f;
1310 if (GreatOrEqual(head, ANGLE_360 - additionalAngle * FLOAT_TWO_ZERO)) {
1311 rotateAngle = -ANGLE_360 + head - tail + additionalAngle * FLOAT_TWO_ZERO;
1312 }
1313 head += (additionalAngle + rotateAngle);
1314 tail += (additionalAngle + rotateAngle);
1315
1316 RSPen pen;
1317 pen.SetAntiAlias(true);
1318 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
1319 pen.SetWidth(thickness);
1320 canvas.Save();
1321
1322 std::vector<GradientColor> gradientColors = GetRingProgressGradientColors();
1323 std::vector<RSColorQuad> colors;
1324 std::vector<float> pos;
1325
1326 for (size_t i = 0; i < gradientColors.size(); i++) {
1327 double dimension = gradientColors[i].GetDimension().Value();
1328 Color color = gradientColors[i].GetLinearColor().BlendOpacity(dimension);
1329 colors.emplace_back(color.GetValue());
1330 pos.emplace_back(gradientColors[i].GetDimension().Value());
1331 }
1332
1333 RSPath path;
1334 #ifndef USE_ROSEN_DRAWING
1335 pen.SetShaderEffect(RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors,
1336 pos, RSTileMode::CLAMP, tail - additionalAngle, head + additionalAngle, nullptr));
1337 #else
1338 pen.SetShaderEffect(
1339 RSRecordingShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos,
1340 RSTileMode::CLAMP, tail - additionalAngle, head + additionalAngle, nullptr));
1341 #endif
1342 RSRect rRect(
1343 centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius);
1344 path.AddArc(rRect, tail, head - tail);
1345 canvas.Rotate(-ANGLE_90 - rotateAngle - additionalAngle, centerPt.GetX(), centerPt.GetY());
1346 canvas.AttachPen(pen);
1347 canvas.DrawPath(path);
1348 canvas.DetachPen();
1349 canvas.Restore();
1350 }
1351
PaintScaleRing(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const1352 void ProgressModifier::PaintScaleRing(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
1353 {
1354 PointF centerPt = PointF(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO) + offset;
1355 double radius = std::min(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO);
1356 double lengthOfScale = strokeWidth_->Get();
1357 double pathDistance = FLOAT_TWO_ZERO * M_PI * radius / scaleCount_->Get();
1358 if (scaleWidth_->Get() > pathDistance) {
1359 PaintRing(canvas, offset, contentSize);
1360 return;
1361 }
1362 double widthOfLine = scaleWidth_->Get();
1363 RSPen pen;
1364 #ifndef USE_ROSEN_DRAWING
1365 RSPath path;
1366 #else
1367 RSRecordingPath path;
1368 #endif
1369 pen.SetWidth(widthOfLine);
1370 path.AddRoundRect(
1371 { 0, 0, widthOfLine, lengthOfScale }, widthOfLine / INT32_TWO, widthOfLine / INT32_TWO,
1372 RSPathDirection::CW_DIRECTION);
1373 pen.SetAntiAlias(true);
1374 pen.SetCapStyle(ToRSCapStyle(LineCap::ROUND));
1375 #ifndef USE_ROSEN_DRAWING
1376 pen.SetPathEffect(RSPathEffect::CreatePathDashEffect(path, pathDistance, 0.0f, RSPathDashStyle::ROTATE));
1377 #else
1378 pen.SetPathEffect(RSRecordingPathEffect::CreatePathDashEffect(path, pathDistance, 0.0f, RSPathDashStyle::ROTATE));
1379 #endif
1380 pen.SetColor(ToRSColor(bgColor_->Get()));
1381 canvas.AttachPen(pen);
1382 if (isRightToLeft_->Get()) {
1383 canvas.Scale(-1, 1);
1384 canvas.Translate(-radius * INT32_TWO, 0);
1385 }
1386 canvas.DrawArc(
1387 { centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1388 ANGLE_270, ANGLE_360);
1389 canvas.DetachPen();
1390 // start to draw cur value progress
1391 pen.SetColor(ToRSColor((color_->Get())));
1392 canvas.AttachPen(pen);
1393 double angle = std::min((value_->Get() / maxValue_->Get()) * ANGLE_360, static_cast<float>(ANGLE_360));
1394 canvas.DrawArc(
1395 { centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1396 ANGLE_270, angle);
1397 canvas.DetachPen();
1398 canvas.Restore();
1399 }
1400
PaintMoon(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const1401 void ProgressModifier::PaintMoon(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
1402 {
1403 static int32_t totalDegree = 1;
1404 PointF centerPt = PointF(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO) + offset;
1405 double radius = std::min(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO);
1406 RSBrush brush;
1407 brush.SetAntiAlias(true);
1408 brush.SetAlpha(true);
1409 brush.SetColor(ToRSColor(bgColor_->Get()));
1410 double angle = std::min((value_->Get() / maxValue_->Get()) * totalDegree, static_cast<float>(totalDegree));
1411 #ifndef USE_ROSEN_DRAWING
1412 RSPath path;
1413 #else
1414 RSRecordingPath path;
1415 #endif
1416 canvas.AttachBrush(brush);
1417 canvas.DrawCircle(ToRSPoint(centerPt), radius);
1418 canvas.DetachBrush();
1419 brush.SetColor(ToRSColor((color_->Get())));
1420 canvas.AttachBrush(brush);
1421 path.AddArc(
1422 { centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1423 ANGLE_90, !isRightToLeft_->Get() ? ANGLE_180 : -ANGLE_180);
1424 if (angle <= FLOAT_ZERO_FIVE) {
1425 double progressOffset = radius - radius * angle / FLOAT_ZERO_FIVE;
1426 path.MoveTo(centerPt.GetX(), centerPt.GetY() - radius);
1427 // startAngle:270 sweepAngle:-180
1428 path.AddArc({ centerPt.GetX() - progressOffset, centerPt.GetY() - radius, centerPt.GetX() + progressOffset,
1429 centerPt.GetY() + radius },
1430 ANGLE_270, !isRightToLeft_->Get() ? -ANGLE_180 : ANGLE_180);
1431 canvas.DrawPath(path);
1432 } else {
1433 double progressOffset = radius * (angle - FLOAT_ZERO_FIVE) / FLOAT_ZERO_FIVE;
1434 path.MoveTo(centerPt.GetX(), centerPt.GetY() - radius);
1435 // startAngle:270 sweepAngle:180
1436 path.AddArc({ centerPt.GetX() - progressOffset, centerPt.GetY() - radius, centerPt.GetX() + progressOffset,
1437 centerPt.GetY() + radius },
1438 ANGLE_270, !isRightToLeft_->Get() ? ANGLE_180 : -ANGLE_180);
1439 canvas.DrawPath(path);
1440 }
1441 canvas.DetachBrush();
1442 }
1443
PaintCapsule(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const1444 void ProgressModifier::PaintCapsule(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
1445 {
1446 auto borderWidth = capsuleBorderWidth_->Get();
1447 if (GreatNotEqual(2 * borderWidth, contentSize.Height())) {
1448 borderWidth = contentSize.Height() / 2;
1449 }
1450 static int32_t totalDegree = 1;
1451 double radiusBig =
1452 std::min((contentSize.Width() - borderWidth) / INT32_TWO, (contentSize.Height() - borderWidth) / INT32_TWO);
1453 double offsetXBig = offset.GetX() + borderWidth / INT32_TWO;
1454 double offsetYBig = offset.GetY() + borderWidth / INT32_TWO;
1455 double radius = std::min(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO);
1456 double offsetX = offset.GetX();
1457 double offsetY = offset.GetY();
1458 double progressWidth =
1459 std::min((value_->Get() / maxValue_->Get()) * totalDegree * contentSize.Width(), contentSize.Width());
1460 RSBrush brush;
1461 brush.SetAntiAlias(true);
1462 RSPen pen;
1463 brush.SetAlpha(true);
1464 brush.SetColor(ToRSColor(bgColor_->Get()));
1465 pen.SetWidth(borderWidth);
1466 pen.SetAntiAlias(true);
1467 pen.SetColor(ToRSColor(borderColor_->Get()));
1468 #ifndef USE_ROSEN_DRAWING
1469 RSPath path;
1470 #else
1471 RSRecordingPath path;
1472 #endif
1473 canvas.AttachBrush(brush);
1474 canvas.DrawRoundRect(
1475 { { offsetX, offsetY, contentSize.Width() + offsetX, contentSize.Height() + offsetY }, radius, radius });
1476 canvas.DetachBrush();
1477 canvas.AttachPen(pen);
1478 if (!NearZero(borderWidth)) {
1479 canvas.DrawRoundRect(
1480 { { offsetXBig, offsetYBig, contentSize.Width() - borderWidth + offsetXBig,
1481 contentSize.Height() - borderWidth + offsetYBig },
1482 radiusBig, radiusBig });
1483 }
1484 canvas.DetachPen();
1485 brush.SetColor(ToRSColor((color_->Get())));
1486 canvas.AttachBrush(brush);
1487 if (!isRightToLeft_->Get()) {
1488 path.AddArc(
1489 { offsetX, offsetY, INT32_TWO * radius + offsetX, contentSize.Height() + offsetY }, ANGLE_90, ANGLE_180);
1490 } else {
1491 path.AddArc({ offsetX + contentSize.Width() - INT32_TWO * radius, offsetY, offsetX + contentSize.Width(),
1492 contentSize.Height() + offsetY },
1493 ANGLE_270, ANGLE_180);
1494 }
1495 if (LessNotEqual(progressWidth, radius)) {
1496 if (!isRightToLeft_->Get()) {
1497 // startAngle:270 sweepAngle:-180
1498 path.AddArc({ offsetX + progressWidth, offsetY, INT32_TWO * radius - progressWidth + offsetX,
1499 contentSize.Height() + offsetY },
1500 ANGLE_270, -ANGLE_180);
1501 } else {
1502 path.AddArc({ offsetX + contentSize.Width() - INT32_TWO * radius + progressWidth, offsetY,
1503 offsetX + contentSize.Width() - progressWidth, contentSize.Height() + offsetY },
1504 ANGLE_90, -ANGLE_180);
1505 }
1506 } else if (GreatNotEqual(progressWidth, contentSize.Width() - radius)) {
1507 path.AddRect(
1508 { offsetX + radius, offsetY, contentSize.Width() + offsetX - radius, contentSize.Height() + offsetY });
1509 if (!isRightToLeft_->Get()) {
1510 // startAngle:270 sweepAngle:180
1511 path.AddArc({ offsetX + (contentSize.Width() - radius) * FLOAT_TWO_ZERO - progressWidth, offsetY,
1512 offsetX + progressWidth, contentSize.Height() + offsetY },
1513 ANGLE_270, ANGLE_180);
1514 } else {
1515 path.AddArc({ offsetX + contentSize.Width() - progressWidth, offsetY,
1516 offsetX - contentSize.Width() + progressWidth + INT32_TWO * radius,
1517 contentSize.Height() + offsetY },
1518 ANGLE_90, ANGLE_180);
1519 }
1520 } else {
1521 if (!isRightToLeft_->Get()) {
1522 path.AddRect({ radius + offsetX, offsetY, progressWidth + offsetX, contentSize.Height() + offsetY });
1523 } else {
1524 path.AddRect({ offsetX + contentSize.Width() - progressWidth, offsetY,
1525 contentSize.Width() + offsetX - radius, contentSize.Height() + offsetY });
1526 }
1527 }
1528 canvas.DrawPath(path);
1529 canvas.DetachBrush();
1530 canvas.Restore();
1531
1532 PaintCapsuleLightSweep(canvas, contentSize, offset, path, false);
1533 }
1534
PaintVerticalCapsule(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const1535 void ProgressModifier::PaintVerticalCapsule(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
1536 {
1537 auto borderWidth = capsuleBorderWidth_->Get();
1538 if (GreatNotEqual(2 * borderWidth, contentSize.Width())) {
1539 borderWidth = contentSize.Width() / 2;
1540 }
1541 static int32_t totalDegree = 1;
1542 double radiusBig =
1543 std::min((contentSize.Width() - borderWidth) / INT32_TWO, (contentSize.Height() - borderWidth) / INT32_TWO);
1544 double offsetXBig = offset.GetX() + borderWidth / INT32_TWO;
1545 double offsetYBig = offset.GetY() + borderWidth / INT32_TWO;
1546 double radius = std::min(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO);
1547 double offsetX = offset.GetX();
1548 double offsetY = offset.GetY();
1549 double progressWidth =
1550 std::min((value_->Get() / maxValue_->Get()) * totalDegree * contentSize.Height(), contentSize.Height());
1551 RSBrush brush;
1552 brush.SetAntiAlias(true);
1553 RSPen pen;
1554 pen.SetWidth(borderWidth);
1555 pen.SetAntiAlias(true);
1556 pen.SetColor(ToRSColor(borderColor_->Get()));
1557 brush.SetAlpha(true);
1558 brush.SetColor(ToRSColor(bgColor_->Get()));
1559 #ifndef USE_ROSEN_DRAWING
1560 RSPath path;
1561 #else
1562 RSRecordingPath path;
1563 #endif
1564 canvas.AttachBrush(brush);
1565 canvas.DrawRoundRect(
1566 { { offsetX, offsetY, contentSize.Width() + offsetX, contentSize.Height() + offsetY }, radius, radius });
1567 canvas.DetachBrush();
1568 canvas.AttachPen(pen);
1569 if (!NearZero(borderWidth)) {
1570 canvas.DrawRoundRect(
1571 { { offsetXBig, offsetYBig, contentSize.Width() - borderWidth + offsetXBig,
1572 contentSize.Height() - borderWidth + offsetYBig },
1573 radiusBig, radiusBig });
1574 }
1575 canvas.DetachPen();
1576 brush.SetColor(ToRSColor((color_->Get())));
1577 canvas.AttachBrush(brush);
1578 path.AddArc(
1579 { offsetX, offsetY, contentSize.Width() + offsetX, contentSize.Width() + offsetY }, 0, -ANGLE_180);
1580 if (LessNotEqual(progressWidth, radius)) {
1581 // startAngle:180 sweepAngle:180
1582 path.AddArc(
1583 { offsetX, offsetY + progressWidth, contentSize.Width() + offsetX,
1584 contentSize.Width() - progressWidth + offsetY },
1585 ANGLE_180, ANGLE_180);
1586 } else if (GreatNotEqual(progressWidth, contentSize.Height() - radius)) {
1587 path.AddRect(
1588 { offsetX, offsetY + radius, contentSize.Width() + offsetX, contentSize.Height() - radius + offsetY });
1589 // startAngle:180 sweepAngle:-180
1590 path.AddArc(
1591 { offsetX, offsetY + (contentSize.Height() - radius) * FLOAT_TWO_ZERO - progressWidth,
1592 contentSize.Width() + offsetX, progressWidth + offsetY },
1593 ANGLE_180, -ANGLE_180);
1594 } else {
1595 path.AddRect(
1596 { offsetX, radius + offsetY, offsetX + contentSize.Width(), progressWidth + offsetY });
1597 }
1598 canvas.DrawPath(path);
1599 canvas.DetachBrush();
1600 canvas.Restore();
1601
1602 PaintCapsuleLightSweep(canvas, contentSize, offset, path, true);
1603 }
1604
PaintCapsuleLightSweep(RSCanvas & canvas,const SizeF & contentSize,const OffsetF & offset,const RSPath & path,bool isVertical) const1605 void ProgressModifier::PaintCapsuleLightSweep(
1606 RSCanvas& canvas, const SizeF& contentSize, const OffsetF& offset, const RSPath& path, bool isVertical) const
1607 {
1608 RSPen pen;
1609 pen.SetAntiAlias(true);
1610 pen.SetWidth(1);
1611 auto gradient = CreateCapsuleGradient();
1612 std::vector<RSColorQuad> colors;
1613 std::vector<float> pos;
1614 RSBrush brush;
1615 auto gradientColors = gradient.GetColors();
1616 for (size_t i = 0; i < gradientColors.size(); i++) {
1617 colors.emplace_back(gradientColors[i].GetColor().GetValue());
1618 pos.emplace_back(gradientColors[i].GetDimension().Value());
1619 }
1620
1621 float endPos = sweepingDate_->Get();
1622 #ifndef USE_ROSEN_DRAWING
1623 if (isVertical) {
1624 brush.SetShaderEffect(RSShaderEffect::CreateLinearGradient(
1625 ToRSPoint(
1626 PointF(offset.GetX() + contentSize.Width() / 2, offset.GetY() + endPos - SWEEP_WIDTH.ConvertToPx())),
1627 ToRSPoint(PointF(offset.GetX() + contentSize.Width() / 2, offset.GetY() + endPos)), colors, pos,
1628 RSTileMode::CLAMP));
1629 } else {
1630 brush.SetShaderEffect(RSShaderEffect::CreateLinearGradient(
1631 ToRSPoint(PointF(!isRightToLeft_->Get() ? offset.GetX() + endPos - SWEEP_WIDTH.ConvertToPx()
1632 : offset.GetX() + contentSize.Width() - endPos,
1633 offset.GetY() + contentSize.Height() / 2)),
1634 ToRSPoint(PointF(!isRightToLeft_->Get()
1635 ? offset.GetX() + endPos
1636 : offset.GetX() + contentSize.Width() - endPos + SWEEP_WIDTH.ConvertToPx(),
1637 offset.GetY() + contentSize.Height() / 2)),
1638 colors, pos, RSTileMode::CLAMP));
1639 }
1640 #else
1641 if (isVertical) {
1642 brush.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
1643 ToRSPoint(PointF(offset.GetX() + contentSize.Width() / 2,
1644 offset.GetY() + endPos - SWEEP_WIDTH.ConvertToPx() + capsuleBorderWidth_->Get())),
1645 ToRSPoint(
1646 PointF(offset.GetX() + contentSize.Width() / 2, offset.GetY() + endPos - capsuleBorderWidth_->Get())),
1647 colors, pos, RSTileMode::CLAMP));
1648 } else {
1649 brush.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
1650 ToRSPoint(PointF(!isRightToLeft_->Get()
1651 ? offset.GetX() + endPos - SWEEP_WIDTH.ConvertToPx() + capsuleBorderWidth_->Get()
1652 : offset.GetX() + contentSize.Width() - endPos + capsuleBorderWidth_->Get(),
1653 offset.GetY() + contentSize.Height() / 2)),
1654 ToRSPoint(PointF(!isRightToLeft_->Get() ? offset.GetX() + endPos - capsuleBorderWidth_->Get()
1655 : offset.GetX() + contentSize.Width() - endPos +
1656 SWEEP_WIDTH.ConvertToPx() - capsuleBorderWidth_->Get(),
1657 offset.GetY() + contentSize.Height() / 2)),
1658 colors, pos, RSTileMode::CLAMP));
1659 }
1660 #endif
1661
1662 auto offsetX = offset.GetX();
1663 auto offsetY = offset.GetY();
1664 canvas.Save();
1665 canvas.ClipPath(path, RSClipOp::INTERSECT, true);
1666 canvas.AttachBrush(brush);
1667 if (isVertical) {
1668 canvas.DrawRect(
1669 { offsetX, offsetY + endPos - SWEEP_WIDTH.ConvertToPx(),
1670 offsetX + contentSize.Width(), offsetY + endPos });
1671 } else {
1672 if (!isRightToLeft_->Get()) {
1673 canvas.DrawRect({ offsetX + endPos - SWEEP_WIDTH.ConvertToPx(), offsetY, offsetX + endPos,
1674 offsetY + contentSize.Height() });
1675 } else {
1676 canvas.DrawRect({ offsetX + contentSize.Width() - endPos, offsetY,
1677 offsetX + contentSize.Width() - endPos + SWEEP_WIDTH.ConvertToPx(), offsetY + contentSize.Height() });
1678 }
1679 }
1680 canvas.DetachBrush();
1681 canvas.Restore();
1682 }
1683
CreateCapsuleGradient() const1684 Gradient ProgressModifier::CreateCapsuleGradient() const
1685 {
1686 Gradient gradient;
1687 Color lightSweepColorBase = Color::WHITE;
1688 Color lightSweepColorEnd = lightSweepColorBase.ChangeOpacity(0.0);
1689 Color lightSweepColorMiddle = lightSweepColorBase.ChangeOpacity(0.2);
1690 GradientColor gradientColorEnd;
1691 isRightToLeft_->Get() ? gradientColorEnd.SetColor(lightSweepColorMiddle)
1692 : gradientColorEnd.SetColor(lightSweepColorEnd);
1693 gradientColorEnd.SetDimension(Dimension(0.0, DimensionUnit::VP));
1694 gradient.AddColor(gradientColorEnd);
1695
1696 GradientColor gradientColorMiddle;
1697 isRightToLeft_->Get() ? gradientColorMiddle.SetColor(lightSweepColorEnd)
1698 : gradientColorMiddle.SetColor(lightSweepColorMiddle);
1699 gradientColorMiddle.SetDimension(SWEEP_WIDTH);
1700 gradient.AddColor(gradientColorMiddle);
1701 return gradient;
1702 }
1703
PostTask(const TaskExecutor::Task & task,const std::string & name)1704 bool ProgressModifier::PostTask(const TaskExecutor::Task& task, const std::string& name)
1705 {
1706 auto pipeline = PipelineBase::GetCurrentContext();
1707 CHECK_NULL_RETURN(pipeline, false);
1708 auto taskExecutor = pipeline->GetTaskExecutor();
1709 CHECK_NULL_RETURN(taskExecutor, false);
1710 return taskExecutor->PostTask(task, TaskExecutor::TaskType::UI, name);
1711 }
1712
SetStrokeRadius(float strokeRaidus)1713 void ProgressModifier::SetStrokeRadius(float strokeRaidus)
1714 {
1715 strokeRadius_->Set(strokeRaidus);
1716 }
1717
SetIsRightToLeft(bool value)1718 void ProgressModifier::SetIsRightToLeft(bool value)
1719 {
1720 if (isRightToLeft_->Get() == value) {
1721 return;
1722 }
1723 isRightToLeft_->Set(value);
1724 }
1725
PaintScaleRingForApiNine(RSCanvas & canvas,const OffsetF & offset,const SizeF & frameSize) const1726 void ProgressModifier::PaintScaleRingForApiNine(RSCanvas& canvas, const OffsetF& offset, const SizeF& frameSize) const
1727 {
1728 auto scaleWidth = scaleWidth_->Get();
1729 PointF centerPt = PointF(frameSize.Width() / 2, frameSize.Height() / 2) + offset;
1730 double radius = std::min(frameSize.Width() / 2, frameSize.Height() / 2);
1731 double lengthOfScale = strokeWidth_->Get();
1732 if (lengthOfScale > radius) {
1733 lengthOfScale = radius / 2;
1734 }
1735 double pathDistance = 2.0 * M_PI * radius / scaleCount_->Get();
1736 if (scaleWidth > pathDistance) {
1737 PaintRing(canvas, offset, frameSize);
1738 return;
1739 }
1740 double widthOfLine = scaleWidth;
1741 RSPen pen;
1742 RSPath path;
1743 pen.SetWidth(widthOfLine);
1744 path.AddRoundRect({
1745 0, 0, widthOfLine, lengthOfScale }, widthOfLine / 2, widthOfLine / 2, RSPathDirection::CW_DIRECTION);
1746 pen.SetAntiAlias(true);
1747 pen.SetCapStyle(ToRSCapStyle(LineCap::ROUND));
1748 pen.SetPathEffect(RSPathEffect::CreatePathDashEffect(path, pathDistance, 0.0f, RSPathDashStyle::ROTATE));
1749 pen.SetColor(ToRSColor(bgColor_->Get()));
1750 canvas.AttachPen(pen);
1751 canvas.DrawArc({
1752 centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1753 ANGLE_270, ANGLE_360);
1754 canvas.DetachPen();
1755 pen.SetColor(ToRSColor((color_->Get())));
1756 canvas.AttachPen(pen);
1757 double angle = (value_->Get() / maxValue_->Get()) * ANGLE_360;
1758 canvas.DrawArc({
1759 centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1760 ANGLE_270, angle);
1761 canvas.DetachPen();
1762 }
1763
PaintCapsuleForApiNine(RSCanvas & canvas,const OffsetF & offset,const SizeF & frameSize) const1764 void ProgressModifier::PaintCapsuleForApiNine(RSCanvas& canvas, const OffsetF& offset, const SizeF& frameSize) const
1765 {
1766 double radius = std::min(frameSize.Width() / 2, frameSize.Height() / 2);
1767 double offsetX = offset.GetX();
1768 double offsetY = offset.GetY();
1769 double progressWidth = (value_->Get() / maxValue_->Get()) * frameSize.Width();
1770 RSBrush brush;
1771 brush.SetAlpha(true);
1772 brush.SetColor(ToRSColor(bgColor_->Get()));
1773 RSPath path;
1774 canvas.AttachBrush(brush);
1775 canvas.DrawRoundRect({ { offsetX, offsetY, frameSize.Width() + offsetX, frameSize.Height() + offsetY },
1776 radius, radius });
1777 canvas.DetachBrush();
1778 brush.SetColor(ToRSColor(color_->Get()));
1779 canvas.AttachBrush(brush);
1780 path.AddArc({ offsetX, offsetY, 2 * radius + offsetX, frameSize.Height() + offsetY }, ANGLE_90, ANGLE_180);
1781 if (LessNotEqual(progressWidth, radius)) {
1782 // startAngle:270 sweepAngle:-180
1783 path.AddArc({
1784 offsetX + progressWidth, offsetY, 2 * radius - progressWidth + offsetX, frameSize.Height() + offsetY },
1785 ANGLE_270, -ANGLE_180);
1786 } else if (GreatNotEqual(progressWidth, frameSize.Width() - radius)) {
1787 path.AddRect({ offsetX + radius, offsetY, frameSize.Width() + offsetX - radius,
1788 frameSize.Height() + offsetY });
1789 // startAngle:270 sweepAngle:180
1790 path.AddArc({ offsetX + (frameSize.Width() - radius) * 2.0 - progressWidth, offsetY, offsetX + progressWidth,
1791 frameSize.Height() + offsetY }, ANGLE_270, ANGLE_180);
1792 } else {
1793 path.AddRect({ radius + offsetX, offsetY, progressWidth + offsetX, frameSize.Height() + offsetY });
1794 }
1795 canvas.DrawPath(path);
1796 canvas.DetachBrush();
1797 }
1798
PaintVerticalCapsuleForApiNine(RSCanvas & canvas,const OffsetF & offset,const SizeF & frameSize) const1799 void ProgressModifier::PaintVerticalCapsuleForApiNine(
1800 RSCanvas& canvas, const OffsetF& offset, const SizeF& frameSize) const
1801 {
1802 double radius = std::min(frameSize.Width() / 2, frameSize.Height() / 2);
1803 double offsetX = offset.GetX();
1804 double offsetY = offset.GetY();
1805 double progressWidth = (value_->Get() / maxValue_->Get()) * frameSize.Height();
1806 RSBrush brush;
1807 brush.SetAlpha(true);
1808 brush.SetColor(ToRSColor(bgColor_->Get()));
1809 RSPath path;
1810 canvas.AttachBrush(brush);
1811 canvas.DrawRoundRect({ {
1812 offsetX, offsetY, frameSize.Width() + offsetX, frameSize.Height() + offsetY }, radius, radius });
1813 canvas.DetachBrush();
1814 brush.SetColor(ToRSColor((color_->Get())));
1815 canvas.AttachBrush(brush);
1816 path.AddArc({ offsetX, offsetY, frameSize.Width() + offsetX, frameSize.Width() + offsetY }, 0, -ANGLE_180);
1817 if (LessNotEqual(progressWidth, radius)) {
1818 // startAngle:180 sweepAngle:180
1819 path.AddArc({ offsetX, offsetY + progressWidth, frameSize.Width() + offsetX,
1820 frameSize.Width() - progressWidth + offsetY },
1821 ANGLE_180, ANGLE_180);
1822 } else if (GreatNotEqual(progressWidth, frameSize.Height() - radius)) {
1823 path.AddRect({
1824 offsetX, offsetY + radius, frameSize.Width() + offsetX, frameSize.Height() - radius + offsetY });
1825 // startAngle:180 sweepAngle:-180
1826 path.AddArc({ offsetX, offsetY + (frameSize.Height() - radius) * 2.0 - progressWidth,
1827 frameSize.Width() + offsetX, progressWidth + offsetY }, ANGLE_180, -ANGLE_180);
1828 } else {
1829 path.AddRect({ offsetX, radius + offsetY, offsetX + frameSize.Width(), progressWidth + offsetY });
1830 }
1831 canvas.DrawPath(path);
1832 canvas.DetachBrush();
1833 }
1834 } // namespace OHOS::Ace::NG
1835