OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/controls/throbber.h" | 5 #include "ui/views/controls/throbber.h" |
6 | 6 |
7 #include "ui/base/resource/resource_bundle.h" | 7 #include "ui/base/resource/resource_bundle.h" |
| 8 #include "ui/gfx/animation/tween.h" |
8 #include "ui/gfx/canvas.h" | 9 #include "ui/gfx/canvas.h" |
9 #include "ui/gfx/image/image.h" | 10 #include "ui/gfx/image/image.h" |
10 #include "ui/gfx/image/image_skia.h" | 11 #include "ui/gfx/image/image_skia.h" |
11 #include "ui/resources/grit/ui_resources.h" | 12 #include "ui/resources/grit/ui_resources.h" |
12 | 13 |
13 namespace views { | 14 namespace views { |
14 | 15 |
15 Throbber::Throbber(int frame_time_ms, bool paint_while_stopped) | 16 Throbber::Throbber(int frame_time_ms, bool paint_while_stopped) |
16 : paint_while_stopped_(paint_while_stopped), | 17 : paint_while_stopped_(paint_while_stopped), |
17 frames_(NULL), | 18 frames_(NULL), |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 IDR_CHECKMARK).ToImageSkia(); | 162 IDR_CHECKMARK).ToImageSkia(); |
162 } | 163 } |
163 | 164 |
164 int checkmark_x = (width() - checkmark_->width()) / 2; | 165 int checkmark_x = (width() - checkmark_->width()) / 2; |
165 int checkmark_y = (height() - checkmark_->height()) / 2; | 166 int checkmark_y = (height() - checkmark_->height()) / 2; |
166 canvas->DrawImageInt(*checkmark_, checkmark_x, checkmark_y); | 167 canvas->DrawImageInt(*checkmark_, checkmark_x, checkmark_y); |
167 } | 168 } |
168 return; | 169 return; |
169 } | 170 } |
170 | 171 |
171 gfx::Rect bounds = GetContentsBounds(); | 172 PaintSpinning(canvas); |
172 // Inset by half the stroke width to make sure the whole arc is inside | 173 } |
173 // the visible rect. | |
174 SkScalar stroke_width = SkIntToScalar(bounds.width()) / 10.0; | |
175 gfx::Rect oval = bounds; | |
176 int inset = SkScalarCeilToInt(stroke_width / 2.0); | |
177 oval.Inset(inset, inset); | |
178 | 174 |
| 175 void MaterialThrobber::PaintSpinning(gfx::Canvas* canvas) { |
| 176 base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time(); |
| 177 |
| 178 // This is a Skia port of the MD spinner SVG. The |start_angle| rotation |
| 179 // here corresponds to the 'rotate' animation. |
| 180 base::TimeDelta rotation_time = base::TimeDelta::FromMilliseconds(1568); |
| 181 int64_t start_angle = 270 + 360 * elapsed_time / rotation_time; |
| 182 |
| 183 // The sweep angle ranges from -|arc_size| to |arc_size| over 1333ms. CSS |
| 184 // animation timing functions apply in between key frames, so we have to |
| 185 // break up the |arc_time| into two keyframes (-arc_size to 0, then 0 to |
| 186 // arc_size). |
| 187 int64_t arc_size = 270; |
| 188 base::TimeDelta arc_time = base::TimeDelta::FromMilliseconds(666); |
| 189 double arc_size_progress = static_cast<double>(elapsed_time.InMicroseconds() % |
| 190 arc_time.InMicroseconds()) / |
| 191 arc_time.InMicroseconds(); |
| 192 // This tween is equivalent to cubic-bezier(0.4, 0.0, 0.2, 1). |
| 193 double sweep = |
| 194 arc_size * gfx::Tween::CalculateValue(gfx::Tween::FAST_OUT_SLOW_IN, |
| 195 arc_size_progress); |
| 196 int64_t sweep_keyframe = (elapsed_time / arc_time) % 2; |
| 197 if (sweep_keyframe == 0) |
| 198 sweep -= arc_size; |
| 199 |
| 200 // This part makes sure the sweep is at least 5 degrees long. Roughly |
| 201 // equivalent to the "magic constants" in SVG's fillunfill animation. |
| 202 const double min_sweep_length = 5.0; |
| 203 if (sweep >= 0.0 && sweep < min_sweep_length) { |
| 204 start_angle -= (min_sweep_length - sweep); |
| 205 sweep = min_sweep_length; |
| 206 } else if (sweep <= 0.0 && sweep > -min_sweep_length) { |
| 207 start_angle += (-min_sweep_length - sweep); |
| 208 sweep = -min_sweep_length; |
| 209 } |
| 210 |
| 211 // To keep the sweep smooth, we have an additional rotation after each |
| 212 // |arc_time| period has elapsed. See SVG's 'rot' animation. |
| 213 int64_t rot_keyframe = (elapsed_time / (arc_time * 2)) % 4; |
| 214 PaintArc(canvas, start_angle + rot_keyframe * arc_size, sweep); |
| 215 } |
| 216 |
| 217 void MaterialThrobber::PaintWaiting(gfx::Canvas* canvas) { |
179 // Calculate start and end points. The angles are counter-clockwise because | 218 // Calculate start and end points. The angles are counter-clockwise because |
180 // the throbber spins counter-clockwise. The finish angle starts at 12 o'clock | 219 // the throbber spins counter-clockwise. The finish angle starts at 12 o'clock |
181 // (90 degrees) and rotates steadily. The start angle trails 180 degrees | 220 // (90 degrees) and rotates steadily. The start angle trails 180 degrees |
182 // behind, except for the first half revolution, when it stays at 12 o'clock. | 221 // behind, except for the first half revolution, when it stays at 12 o'clock. |
183 base::TimeDelta revolution_time = base::TimeDelta::FromMilliseconds(1320); | 222 base::TimeDelta revolution_time = base::TimeDelta::FromMilliseconds(1320); |
184 base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time(); | 223 base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time(); |
185 int64_t twelve_oclock = 90; | 224 int64_t twelve_oclock = 90; |
186 int64_t finish_angle = twelve_oclock + 360 * elapsed_time / revolution_time; | 225 int64_t finish_angle = twelve_oclock + 360 * elapsed_time / revolution_time; |
187 int64_t start_angle = std::max(finish_angle - 180, twelve_oclock); | 226 int64_t start_angle = std::max(finish_angle - 180, twelve_oclock); |
188 | 227 |
| 228 // Negate the angles to convert to the clockwise numbers Skia expects. |
| 229 PaintArc(canvas, -start_angle, -(finish_angle - start_angle)); |
| 230 } |
| 231 |
| 232 void MaterialThrobber::PaintArc(gfx::Canvas* canvas, |
| 233 SkScalar start_angle, |
| 234 SkScalar sweep) { |
| 235 gfx::Rect bounds = GetContentsBounds(); |
| 236 // Inset by half the stroke width to make sure the whole arc is inside |
| 237 // the visible rect. |
| 238 SkScalar stroke_width = SkIntToScalar(bounds.width()) / 10.0; |
| 239 gfx::Rect oval = bounds; |
| 240 int inset = SkScalarCeilToInt(stroke_width / 2.0); |
| 241 oval.Inset(inset, inset); |
| 242 |
189 SkPath path; | 243 SkPath path; |
190 // Negate the angles to convert to the clockwise numbers Skia expects. | 244 path.arcTo(gfx::RectToSkRect(oval), start_angle, sweep, true); |
191 path.arcTo(gfx::RectToSkRect(oval), -start_angle, | |
192 -(finish_angle - start_angle), true); | |
193 | 245 |
194 SkPaint paint; | 246 SkPaint paint; |
195 // TODO(estade): find a place for this color. | 247 // TODO(estade): find a place for this color. |
196 paint.setColor(SkColorSetRGB(0x42, 0x85, 0xF4)); | 248 paint.setColor(SkColorSetRGB(0x42, 0x85, 0xF4)); |
197 paint.setStrokeCap(SkPaint::kRound_Cap); | 249 paint.setStrokeCap(SkPaint::kRound_Cap); |
198 paint.setStrokeWidth(stroke_width); | 250 paint.setStrokeWidth(stroke_width); |
199 paint.setStyle(SkPaint::kStroke_Style); | 251 paint.setStyle(SkPaint::kStroke_Style); |
200 paint.setAntiAlias(true); | 252 paint.setAntiAlias(true); |
201 canvas->DrawPath(path, paint); | 253 canvas->DrawPath(path, paint); |
202 } | 254 } |
203 | 255 |
204 } // namespace views | 256 } // namespace views |
OLD | NEW |