| 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 |