Index: ui/views/controls/throbber.cc |
diff --git a/ui/views/controls/throbber.cc b/ui/views/controls/throbber.cc |
index 7b513c7d404461a2c8001f61f244b7d6843aba21..b2a26fccc162584649cbaf0681980b0ed9746997 100644 |
--- a/ui/views/controls/throbber.cc |
+++ b/ui/views/controls/throbber.cc |
@@ -5,6 +5,7 @@ |
#include "ui/views/controls/throbber.h" |
#include "ui/base/resource/resource_bundle.h" |
+#include "ui/gfx/animation/tween.h" |
#include "ui/gfx/canvas.h" |
#include "ui/gfx/image/image.h" |
#include "ui/gfx/image/image_skia.h" |
@@ -168,14 +169,52 @@ void MaterialThrobber::OnPaint(gfx::Canvas* canvas) { |
return; |
} |
- gfx::Rect bounds = GetContentsBounds(); |
- // Inset by half the stroke width to make sure the whole arc is inside |
- // the visible rect. |
- SkScalar stroke_width = SkIntToScalar(bounds.width()) / 10.0; |
- gfx::Rect oval = bounds; |
- int inset = SkScalarCeilToInt(stroke_width / 2.0); |
- oval.Inset(inset, inset); |
+ PaintSpinning(canvas); |
+} |
+ |
+void MaterialThrobber::PaintSpinning(gfx::Canvas* canvas) { |
+ base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time(); |
+ |
+ // This is a Skia port of the MD spinner SVG. The |start_angle| rotation |
+ // here corresponds to the 'rotate' animation. |
+ base::TimeDelta rotation_time = base::TimeDelta::FromMilliseconds(1568); |
+ int64_t start_angle = 270 + 360 * elapsed_time / rotation_time; |
+ |
+ // The sweep angle ranges from -|arc_size| to |arc_size| over 1333ms. CSS |
+ // animation timing functions apply in between key frames, so we have to |
+ // break up the |arc_time| into two keyframes (-arc_size to 0, then 0 to |
+ // arc_size). |
+ int64_t arc_size = 270; |
+ base::TimeDelta arc_time = base::TimeDelta::FromMilliseconds(666); |
+ double arc_size_progress = static_cast<double>(elapsed_time.InMicroseconds() % |
+ arc_time.InMicroseconds()) / |
+ arc_time.InMicroseconds(); |
+ // This tween is equivalent to cubic-bezier(0.4, 0.0, 0.2, 1). |
+ double sweep = |
+ arc_size * gfx::Tween::CalculateValue(gfx::Tween::FAST_OUT_SLOW_IN, |
+ arc_size_progress); |
+ int64_t sweep_keyframe = (elapsed_time / arc_time) % 2; |
+ if (sweep_keyframe == 0) |
+ sweep -= arc_size; |
+ |
+ // This part makes sure the sweep is at least 5 degrees long. Roughly |
+ // equivalent to the "magic constants" in SVG's fillunfill animation. |
+ const double min_sweep_length = 5.0; |
+ if (sweep >= 0.0 && sweep < min_sweep_length) { |
+ start_angle -= (min_sweep_length - sweep); |
+ sweep = min_sweep_length; |
+ } else if (sweep <= 0.0 && sweep > -min_sweep_length) { |
+ start_angle += (-min_sweep_length - sweep); |
+ sweep = -min_sweep_length; |
+ } |
+ |
+ // To keep the sweep smooth, we have an additional rotation after each |
+ // |arc_time| period has elapsed. See SVG's 'rot' animation. |
+ int64_t rot_keyframe = (elapsed_time / (arc_time * 2)) % 4; |
+ PaintArc(canvas, start_angle + rot_keyframe * arc_size, sweep); |
+} |
+void MaterialThrobber::PaintWaiting(gfx::Canvas* canvas) { |
// Calculate start and end points. The angles are counter-clockwise because |
// the throbber spins counter-clockwise. The finish angle starts at 12 o'clock |
// (90 degrees) and rotates steadily. The start angle trails 180 degrees |
@@ -186,10 +225,23 @@ void MaterialThrobber::OnPaint(gfx::Canvas* canvas) { |
int64_t finish_angle = twelve_oclock + 360 * elapsed_time / revolution_time; |
int64_t start_angle = std::max(finish_angle - 180, twelve_oclock); |
- SkPath path; |
// Negate the angles to convert to the clockwise numbers Skia expects. |
- path.arcTo(gfx::RectToSkRect(oval), -start_angle, |
- -(finish_angle - start_angle), true); |
+ PaintArc(canvas, -start_angle, -(finish_angle - start_angle)); |
+} |
+ |
+void MaterialThrobber::PaintArc(gfx::Canvas* canvas, |
+ SkScalar start_angle, |
+ SkScalar sweep) { |
+ gfx::Rect bounds = GetContentsBounds(); |
+ // Inset by half the stroke width to make sure the whole arc is inside |
+ // the visible rect. |
+ SkScalar stroke_width = SkIntToScalar(bounds.width()) / 10.0; |
+ gfx::Rect oval = bounds; |
+ int inset = SkScalarCeilToInt(stroke_width / 2.0); |
+ oval.Inset(inset, inset); |
+ |
+ SkPath path; |
+ path.arcTo(gfx::RectToSkRect(oval), start_angle, sweep, true); |
SkPaint paint; |
// TODO(estade): find a place for this color. |