1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/gfx/paint_throbber.h"
7 #include "base/time/time.h"
8 #include "ui/gfx/animation/tween.h"
9 #include "ui/gfx/canvas.h"
10 #include "ui/gfx/geometry/rect.h"
11 #include "ui/gfx/skia_util.h"
17 void PaintArc(Canvas
* canvas
,
22 // Stroke width depends on size.
23 // . For size < 28: 3 - (28 - size) / 16
24 // . For 28 <= size: (8 + size) / 12
25 SkScalar stroke_width
= bounds
.width() < 28
26 ? 3.0 - SkIntToScalar(28 - bounds
.width()) / 16.0
27 : SkIntToScalar(bounds
.width() + 8) / 12.0;
29 // Inset by half the stroke width to make sure the whole arc is inside
31 int inset
= SkScalarCeilToInt(stroke_width
/ 2.0);
32 oval
.Inset(inset
, inset
);
35 path
.arcTo(RectToSkRect(oval
), start_angle
, sweep
, true);
38 paint
.setColor(color
);
39 paint
.setStrokeCap(SkPaint::kRound_Cap
);
40 paint
.setStrokeWidth(stroke_width
);
41 paint
.setStyle(SkPaint::kStroke_Style
);
42 paint
.setAntiAlias(true);
43 canvas
->DrawPath(path
, paint
);
48 void PaintThrobberSpinning(Canvas
* canvas
,
49 const Rect
& bounds
, SkColor color
, const base::TimeDelta
& elapsed_time
) {
50 // This is a Skia port of the MD spinner SVG. The |start_angle| rotation
51 // here corresponds to the 'rotate' animation.
52 base::TimeDelta rotation_time
= base::TimeDelta::FromMilliseconds(1568);
53 int64_t start_angle
= 270 + 360 * elapsed_time
/ rotation_time
;
55 // The sweep angle ranges from -|arc_size| to |arc_size| over 1333ms. CSS
56 // animation timing functions apply in between key frames, so we have to
57 // break up the |arc_time| into two keyframes (-arc_size to 0, then 0 to
59 int64_t arc_size
= 270;
60 base::TimeDelta arc_time
= base::TimeDelta::FromMilliseconds(666);
61 double arc_size_progress
= static_cast<double>(elapsed_time
.InMicroseconds() %
62 arc_time
.InMicroseconds()) /
63 arc_time
.InMicroseconds();
64 // This tween is equivalent to cubic-bezier(0.4, 0.0, 0.2, 1).
66 arc_size
* Tween::CalculateValue(Tween::FAST_OUT_SLOW_IN
,
68 int64_t sweep_keyframe
= (elapsed_time
/ arc_time
) % 2;
69 if (sweep_keyframe
== 0)
72 // This part makes sure the sweep is at least 5 degrees long. Roughly
73 // equivalent to the "magic constants" in SVG's fillunfill animation.
74 const double min_sweep_length
= 5.0;
75 if (sweep
>= 0.0 && sweep
< min_sweep_length
) {
76 start_angle
-= (min_sweep_length
- sweep
);
77 sweep
= min_sweep_length
;
78 } else if (sweep
<= 0.0 && sweep
> -min_sweep_length
) {
79 start_angle
+= (-min_sweep_length
- sweep
);
80 sweep
= -min_sweep_length
;
83 // To keep the sweep smooth, we have an additional rotation after each
84 // |arc_time| period has elapsed. See SVG's 'rot' animation.
85 int64_t rot_keyframe
= (elapsed_time
/ (arc_time
* 2)) % 4;
86 PaintArc(canvas
, bounds
, color
,
87 start_angle
+ rot_keyframe
* arc_size
, sweep
);
90 void PaintThrobberSpinningForFrame(Canvas
* canvas
,
91 const Rect
& bounds
, SkColor color
, uint32_t frame
) {
92 const uint32_t frame_duration_ms
= 30;
93 PaintThrobberSpinning(canvas
, bounds
, color
,
94 base::TimeDelta::FromMilliseconds(frame
* frame_duration_ms
));
97 void PaintThrobberWaiting(Canvas
* canvas
,
98 const Rect
& bounds
, SkColor color
, const base::TimeDelta
& elapsed_time
) {
99 // Calculate start and end points. The angles are counter-clockwise because
100 // the throbber spins counter-clockwise. The finish angle starts at 12 o'clock
101 // (90 degrees) and rotates steadily. The start angle trails 180 degrees
102 // behind, except for the first half revolution, when it stays at 12 o'clock.
103 base::TimeDelta revolution_time
= base::TimeDelta::FromMilliseconds(1320);
104 int64_t twelve_oclock
= 90;
105 int64_t finish_angle
= twelve_oclock
+ 360 * elapsed_time
/ revolution_time
;
106 int64_t start_angle
= std::max(finish_angle
- 180, twelve_oclock
);
108 // Negate the angles to convert to the clockwise numbers Skia expects.
109 PaintArc(canvas
, bounds
, color
, -start_angle
, -(finish_angle
- start_angle
));
112 void PaintThrobberWaitingForFrame(Canvas
* canvas
,
113 const Rect
& bounds
, SkColor color
, uint32_t frame
) {
114 const uint32_t frame_duration_ms
= 30;
115 PaintThrobberWaiting(canvas
, bounds
, color
,
116 base::TimeDelta::FromMilliseconds(frame
* frame_duration_ms
));