Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Unified Diff: ash/shelf/voice_interaction_overlay.cc

Issue 2972923002: Update voice interaction burst animation (Closed)
Patch Set: fix comments Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ash/shelf/voice_interaction_overlay.cc
diff --git a/ash/shelf/voice_interaction_overlay.cc b/ash/shelf/voice_interaction_overlay.cc
index e50c738ec3d0b156719ea2738e17b28c83a09d0b..c88620a9d081c2e618207757bff5dd6ee209bbb4 100644
--- a/ash/shelf/voice_interaction_overlay.cc
+++ b/ash/shelf/voice_interaction_overlay.cc
@@ -6,6 +6,7 @@
#include <algorithm>
#include <memory>
+#include <string>
#include <utility>
#include "ash/public/cpp/shelf_types.h"
@@ -26,6 +27,7 @@
#include "ui/accessibility/ax_node_data.h"
#include "ui/app_list/presenter/app_list.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/compositor/callback_layer_animation_observer.h"
#include "ui/compositor/layer_animation_element.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animation_sequence.h"
@@ -43,6 +45,7 @@
#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
#include "ui/views/animation/ink_drop_impl.h"
#include "ui/views/animation/ink_drop_mask.h"
+#include "ui/views/animation/ink_drop_painted_layer_delegates.h"
#include "ui/views/painter.h"
#include "ui/views/widget/widget.h"
@@ -62,28 +65,87 @@ constexpr int kRippleOpacityDurationMs = 100;
constexpr int kRippleOpacityRetractDurationMs = 200;
constexpr float kRippleOpacity = 0.2f;
-constexpr float kIconInitSizeDip = 24.f;
+constexpr float kIconInitSizeDip = 48.f;
constexpr float kIconStartSizeDip = 4.f;
constexpr float kIconSizeDip = 24.f;
+constexpr float kIconEndSizeDip = 48.f;
constexpr float kIconOffsetDip = 56.f;
constexpr float kIconOpacity = 1.f;
-constexpr int kIconBurstDurationMs = 100;
constexpr float kBackgroundInitSizeDip = 48.f;
constexpr float kBackgroundStartSizeDip = 10.f;
constexpr float kBackgroundSizeDip = 48.f;
constexpr int kBackgroundOpacityDurationMs = 200;
constexpr float kBackgroundShadowElevationDip = 24.f;
+// TODO(xiaohuic): this is 2x device size, 1x actually have a different size.
+// Need to figure out a way to dynamically change sizes.
+constexpr float kBackgroundLargeWidthDip = 352.5f;
+constexpr float kBackgroundLargeHeightDip = 540.0f;
+constexpr float kBackgroundCornerRadiusDip = 2.f;
+constexpr float kBackgroundPaddingDip = 6.f;
+constexpr int kBackgroundMorphDurationMs = 300;
+
+constexpr int kHideDurationMs = 200;
+
+// The minimum scale factor to use when scaling rectangle layers. Smaller values
+// were causing visual anomalies.
+constexpr float kMinimumRectScale = 0.0001f;
+
+// The minimum scale factor to use when scaling circle layers. Smaller values
+// were causing visual anomalies.
+constexpr float kMinimumCircleScale = 0.001f;
+
+class VoiceInteractionIcon : public ui::Layer, public ui::LayerDelegate {
+ public:
+ VoiceInteractionIcon() {
+ set_name("VoiceInteractionOverlay:ICON_LAYER");
+ SetBounds(gfx::Rect(0, 0, kIconInitSizeDip, kIconInitSizeDip));
+ SetFillsBoundsOpaquely(false);
+ SetMasksToBounds(false);
+ set_delegate(this);
+ }
+
+ private:
+ // ui::LayerDelegate:
+ void OnPaintLayer(const ui::PaintContext& context) override {
+ ui::PaintRecorder recorder(context, size());
+ gfx::PaintVectorIcon(recorder.canvas(), kShelfVoiceInteractionIcon,
+ kIconInitSizeDip, 0);
+ }
+
+ void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
+
+ void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
+
+ DISALLOW_COPY_AND_ASSIGN(VoiceInteractionIcon);
+};
+} // namespace
class VoiceInteractionIconBackground : public ui::Layer,
public ui::LayerDelegate {
public:
- VoiceInteractionIconBackground() : Layer(ui::LAYER_NOT_DRAWN) {
+ VoiceInteractionIconBackground()
+ : Layer(ui::LAYER_NOT_DRAWN),
+ large_size_(
+ gfx::Size(kBackgroundLargeWidthDip, kBackgroundLargeHeightDip)),
+ small_size_(gfx::Size(kBackgroundSizeDip, kBackgroundSizeDip)),
+ center_point_(
oshima 2017/07/06 22:04:42 I was thinking something like HalfSize(small_siz
xc 2017/07/07 16:38:15 I see, some of the places are getting points, some
+ gfx::PointF(kBackgroundSizeDip / 2, kBackgroundSizeDip / 2)),
+ circle_layer_delegate_(
+ new views::CircleLayerDelegate(SK_ColorWHITE,
+ kBackgroundSizeDip / 2)),
+ rect_layer_delegate_(
+ new views::RectangleLayerDelegate(SK_ColorWHITE,
oshima 2017/07/06 22:04:42 MakeUnique
xc 2017/07/07 16:38:15 Done.
+ gfx::SizeF(small_size_))) {
set_name("VoiceInteractionOverlay:BACKGROUND_LAYER");
SetBounds(gfx::Rect(0, 0, kBackgroundInitSizeDip, kBackgroundInitSizeDip));
SetFillsBoundsOpaquely(false);
SetMasksToBounds(false);
+ for (int i = 0; i < static_cast<int>(PaintedShape::PAINTED_SHAPE_COUNT);
+ ++i)
oshima 2017/07/06 22:04:42 alternatively, you can define const PaintedShape
xc 2017/07/07 16:38:15 Done.
+ AddPaintLayer(static_cast<PaintedShape>(i));
+
shadow_values_ =
gfx::ShadowValue::MakeMdShadowValues(kBackgroundShadowElevationDip);
const gfx::Insets shadow_margin =
@@ -100,7 +162,238 @@ class VoiceInteractionIconBackground : public ui::Layer,
}
~VoiceInteractionIconBackground() override{};
+ void AnimateToLarge(const gfx::PointF& new_center,
+ ui::LayerAnimationObserver* animation_observer) {
+ PaintedShapeTransforms transforms;
+ // Setup the painted layers to be the small round size and show it
+ CalculateCircleTransforms(small_size_, &transforms);
+ SetTransforms(transforms);
+ SetPaintedLayersVisible(true);
+
+ // Hide the shadow layer
+ shadow_layer_->SetVisible(false);
+
+ center_point_ = new_center;
+ // Animate the painted layers to the large rectangle size
+ CalculateRectTransforms(large_size_, kBackgroundCornerRadiusDip,
+ &transforms);
+
+ AnimateToTransforms(
+ transforms,
+ base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs),
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
+ gfx::Tween::LINEAR_OUT_SLOW_IN, animation_observer);
+ }
+
+ void ResetShape() {
+ // This reverts to the original small round shape.
+ shadow_layer_->SetVisible(true);
+ SetPaintedLayersVisible(false);
+ center_point_.SetPoint(small_size_.width() / 2.f,
+ small_size_.height() / 2.f);
+ }
+
private:
+ // Enumeration of the different shapes that compose the background.
+ enum class PaintedShape {
+ TOP_LEFT_CIRCLE = 0,
+ TOP_RIGHT_CIRCLE,
+ BOTTOM_RIGHT_CIRCLE,
+ BOTTOM_LEFT_CIRCLE,
+ HORIZONTAL_RECT,
+ VERTICAL_RECT,
+ // The total number of shapes, not an actual shape.
+ PAINTED_SHAPE_COUNT
+ };
oshima 2017/07/07 19:15:38 How about moving this to anonymous class, and defi
xc 2017/07/07 23:35:39 changed back to enum.
+
+ typedef gfx::Transform PaintedShapeTransforms[static_cast<int>(
+ PaintedShape::PAINTED_SHAPE_COUNT)];
+
+ void AddPaintLayer(PaintedShape painted_shape) {
+ ui::LayerDelegate* delegate = nullptr;
+ switch (painted_shape) {
+ case PaintedShape::TOP_LEFT_CIRCLE:
+ case PaintedShape::TOP_RIGHT_CIRCLE:
+ case PaintedShape::BOTTOM_RIGHT_CIRCLE:
+ case PaintedShape::BOTTOM_LEFT_CIRCLE:
+ delegate = circle_layer_delegate_.get();
+ break;
+ case PaintedShape::HORIZONTAL_RECT:
+ case PaintedShape::VERTICAL_RECT:
+ delegate = rect_layer_delegate_.get();
+ break;
+ case PaintedShape::PAINTED_SHAPE_COUNT:
+ NOTREACHED() << "PAINTED_SHAPE_COUNT is not an actual shape type.";
+ break;
+ }
+
+ ui::Layer* layer = new ui::Layer();
+ Add(layer);
+
+ layer->SetBounds(gfx::Rect(small_size_));
+ layer->SetFillsBoundsOpaquely(false);
+ layer->set_delegate(delegate);
+ layer->SetVisible(true);
+ layer->SetOpacity(1.0);
+ layer->SetMasksToBounds(false);
+ layer->set_name("PAINTED_SHAPE_COUNT:" + ToLayerName(painted_shape));
+
+ painted_layers_[static_cast<int>(painted_shape)].reset(layer);
+ }
+
+ void SetTransforms(const PaintedShapeTransforms transforms) {
+ for (int i = 0; i < static_cast<int>(PaintedShape::PAINTED_SHAPE_COUNT);
+ ++i)
oshima 2017/07/07 19:15:37 kAllShapse
xc 2017/07/07 23:35:39 changed back to enum
+ painted_layers_[i]->SetTransform(transforms[i]);
+ }
+
+ void SetPaintedLayersVisible(bool visible) {
+ for (int i = 0; i < static_cast<int>(PaintedShape::PAINTED_SHAPE_COUNT);
+ ++i)
oshima 2017/07/07 19:15:37 ditto
xc 2017/07/07 23:35:39 changed back to enum
+ painted_layers_[i]->SetVisible(visible);
+ }
+
+ void CalculateCircleTransforms(const gfx::Size& size,
+ PaintedShapeTransforms* transforms_out) const {
+ CalculateRectTransforms(size, std::min(size.width(), size.height()) / 2.0f,
+ transforms_out);
+ }
+
+ void CalculateRectTransforms(const gfx::Size& desired_size,
+ float corner_radius,
+ PaintedShapeTransforms* transforms_out) const {
+ DCHECK_GE(desired_size.width() / 2.0f, corner_radius)
+ << "The circle's diameter should not be greater than the total width.";
+ DCHECK_GE(desired_size.height() / 2.0f, corner_radius)
+ << "The circle's diameter should not be greater than the total height.";
+
+ gfx::SizeF size(desired_size);
+ // This function can be called before the layer's been added to a view,
+ // either at construction time or in tests.
+ if (GetCompositor()) {
+ // Modify |desired_size| so that the ripple aligns to pixel bounds.
+ const float dsf = GetCompositor()->device_scale_factor();
+ gfx::RectF ripple_bounds((gfx::PointF(center_point_)), gfx::SizeF());
+ ripple_bounds.Inset(-gfx::InsetsF(desired_size.height() / 2.0f,
+ desired_size.width() / 2.0f));
+ ripple_bounds.Scale(dsf);
+ ripple_bounds = gfx::RectF(gfx::ToEnclosingRect(ripple_bounds));
oshima 2017/07/06 22:04:42 gfx::ScaleToEnclosingRect?
xc 2017/07/07 16:38:15 This one does not support RectF
oshima 2017/07/07 19:15:38 Doh, too bad.
xc 2017/07/07 23:35:39 Acknowledged.
+ ripple_bounds.Scale(1.0f / dsf);
+ size = ripple_bounds.size();
+ }
+
+ // The shapes are drawn such that their center points are not at the origin.
+ // Thus we use the CalculateCircleTransform() and CalculateRectTransform()
+ // methods to calculate the complex Transforms.
+
+ const float circle_scale = std::max(
+ kMinimumCircleScale,
+ corner_radius / static_cast<float>(circle_layer_delegate_->radius()));
+
+ const float circle_target_x_offset = size.width() / 2.0f - corner_radius;
+ const float circle_target_y_offset = size.height() / 2.0f - corner_radius;
+
+ (*transforms_out)[static_cast<int>(PaintedShape::TOP_LEFT_CIRCLE)] =
+ CalculateCircleTransform(circle_scale, -circle_target_x_offset,
+ -circle_target_y_offset);
+ (*transforms_out)[static_cast<int>(PaintedShape::TOP_RIGHT_CIRCLE)] =
+ CalculateCircleTransform(circle_scale, circle_target_x_offset,
+ -circle_target_y_offset);
+ (*transforms_out)[static_cast<int>(PaintedShape::BOTTOM_RIGHT_CIRCLE)] =
+ CalculateCircleTransform(circle_scale, circle_target_x_offset,
+ circle_target_y_offset);
+ (*transforms_out)[static_cast<int>(PaintedShape::BOTTOM_LEFT_CIRCLE)] =
+ CalculateCircleTransform(circle_scale, -circle_target_x_offset,
+ circle_target_y_offset);
+
+ const float rect_delegate_width = rect_layer_delegate_->size().width();
+ const float rect_delegate_height = rect_layer_delegate_->size().height();
+
+ (*transforms_out)[static_cast<int>(PaintedShape::HORIZONTAL_RECT)] =
+ CalculateRectTransform(
+ std::max(kMinimumRectScale, size.width() / rect_delegate_width),
+ std::max(kMinimumRectScale, (size.height() - 2.0f * corner_radius) /
+ rect_delegate_height));
+
+ (*transforms_out)[static_cast<int>(PaintedShape::VERTICAL_RECT)] =
+ CalculateRectTransform(
+ std::max(kMinimumRectScale, (size.width() - 2.0f * corner_radius) /
+ rect_delegate_width),
+ std::max(kMinimumRectScale, size.height() / rect_delegate_height));
+ }
+
+ gfx::Transform CalculateCircleTransform(float scale,
+ float target_center_x,
+ float target_center_y) const {
+ gfx::Transform transform;
+ // Offset for the center point of the ripple.
+ transform.Translate(center_point_.x(), center_point_.y());
+ // Move circle to target.
+ transform.Translate(target_center_x, target_center_y);
+ transform.Scale(scale, scale);
+ // Align center point of the painted circle.
+ const gfx::Vector2dF circle_center_offset =
+ circle_layer_delegate_->GetCenteringOffset();
+ transform.Translate(-circle_center_offset.x(), -circle_center_offset.y());
+ return transform;
+ }
+
+ gfx::Transform CalculateRectTransform(float x_scale, float y_scale) const {
+ gfx::Transform transform;
+ transform.Translate(center_point_.x(), center_point_.y());
+ transform.Scale(x_scale, y_scale);
+ const gfx::Vector2dF rect_center_offset =
+ rect_layer_delegate_->GetCenteringOffset();
+ transform.Translate(-rect_center_offset.x(), -rect_center_offset.y());
+ return transform;
+ }
+
+ void AnimateToTransforms(
+ const PaintedShapeTransforms transforms,
+ base::TimeDelta duration,
+ ui::LayerAnimator::PreemptionStrategy preemption_strategy,
+ gfx::Tween::Type tween,
+ ui::LayerAnimationObserver* animation_observer) {
+ for (int i = 0; i < static_cast<int>(PaintedShape::PAINTED_SHAPE_COUNT);
+ ++i) {
+ ui::LayerAnimator* animator = painted_layers_[i]->GetAnimator();
+ ui::ScopedLayerAnimationSettings animation(animator);
+ animation.SetPreemptionStrategy(preemption_strategy);
+ animation.SetTweenType(tween);
+ std::unique_ptr<ui::LayerAnimationElement> element =
+ ui::LayerAnimationElement::CreateTransformElement(transforms[i],
+ duration);
+ ui::LayerAnimationSequence* sequence =
+ new ui::LayerAnimationSequence(std::move(element));
+
+ if (animation_observer)
+ sequence->AddObserver(animation_observer);
+
+ animator->StartAnimation(sequence);
+ }
+ }
+
+ std::string ToLayerName(PaintedShape painted_shape) {
+ switch (painted_shape) {
+ case PaintedShape::TOP_LEFT_CIRCLE:
+ return "TOP_LEFT_CIRCLE";
+ case PaintedShape::TOP_RIGHT_CIRCLE:
+ return "TOP_RIGHT_CIRCLE";
+ case PaintedShape::BOTTOM_RIGHT_CIRCLE:
+ return "BOTTOM_RIGHT_CIRCLE";
+ case PaintedShape::BOTTOM_LEFT_CIRCLE:
+ return "BOTTOM_LEFT_CIRCLE";
+ case PaintedShape::HORIZONTAL_RECT:
+ return "HORIZONTAL_RECT";
+ case PaintedShape::VERTICAL_RECT:
+ return "VERTICAL_RECT";
+ case PaintedShape::PAINTED_SHAPE_COUNT:
+ NOTREACHED() << "The PAINTED_SHAPE_COUNT value should never be used.";
+ return "PAINTED_SHAPE_COUNT";
+ }
+ return "UNKNOWN";
+ }
+
void OnPaintLayer(const ui::PaintContext& context) override {
// Radius is based on the parent layer size, the shadow layer is expanded
// to make room for the shadow.
@@ -124,37 +417,30 @@ class VoiceInteractionIconBackground : public ui::Layer,
void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
- gfx::ShadowValues shadow_values_;
+ // ui::Layers for all of the painted shape layers that compose the morphing
+ // shape.
+ std::unique_ptr<ui::Layer>
+ painted_layers_[static_cast<int>(PaintedShape::PAINTED_SHAPE_COUNT)];
oshima 2017/07/06 22:04:42 alternatively, you can do std::map<PaintedShape,
xc 2017/07/07 16:38:15 isn't map much slower than just indexing an array?
oshima 2017/07/07 19:15:39 std::map is implemented as a tree and accessing it
xc 2017/07/07 23:35:39 changed back to enum as discussed offline.
- std::unique_ptr<ui::Layer> shadow_layer_;
+ const gfx::Size large_size_;
- DISALLOW_COPY_AND_ASSIGN(VoiceInteractionIconBackground);
-};
+ const gfx::Size small_size_;
oshima 2017/07/06 22:04:42 looks like SizeF is better choice?
xc 2017/07/07 16:38:15 I have to use this to set the layer bounds, which
-class VoiceInteractionIcon : public ui::Layer, public ui::LayerDelegate {
- public:
- VoiceInteractionIcon() {
- set_name("VoiceInteractionOverlay:ICON_LAYER");
- SetBounds(gfx::Rect(0, 0, kIconInitSizeDip, kIconInitSizeDip));
- SetFillsBoundsOpaquely(false);
- SetMasksToBounds(false);
- set_delegate(this);
- }
+ // The center point of the painted shape.
+ gfx::PointF center_point_;
- private:
- void OnPaintLayer(const ui::PaintContext& context) override {
- ui::PaintRecorder recorder(context, size());
- gfx::PaintVectorIcon(recorder.canvas(), kShelfVoiceInteractionIcon,
- kIconInitSizeDip, 0);
- }
+ // ui::LayerDelegate to paint circles for all the circle layers.
+ std::unique_ptr<views::CircleLayerDelegate> circle_layer_delegate_;
- void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
+ // ui::LayerDelegate to paint rectangles for all the rectangle layers.
+ std::unique_ptr<views::RectangleLayerDelegate> rect_layer_delegate_;
- void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
+ gfx::ShadowValues shadow_values_;
- DISALLOW_COPY_AND_ASSIGN(VoiceInteractionIcon);
+ std::unique_ptr<ui::Layer> shadow_layer_;
+
+ DISALLOW_COPY_AND_ASSIGN(VoiceInteractionIconBackground);
};
-} // namespace
VoiceInteractionOverlay::VoiceInteractionOverlay(AppListButton* host_view)
: ripple_layer_(new ui::Layer()),
@@ -182,8 +468,9 @@ VoiceInteractionOverlay::VoiceInteractionOverlay(AppListButton* host_view)
VoiceInteractionOverlay::~VoiceInteractionOverlay() {}
-void VoiceInteractionOverlay::StartAnimation() {
+void VoiceInteractionOverlay::StartAnimation(bool show_icon) {
is_bursting_ = false;
+ show_icon_ = show_icon;
SetVisible(true);
// Setup ripple initial state.
@@ -219,14 +506,13 @@ void VoiceInteractionOverlay::StartAnimation() {
ripple_layer_->SetOpacity(kRippleOpacity);
}
- // We need to determine the animation direction based on shelf alignment.
- ShelfAlignment alignment =
- Shelf::ForWindow(host_view_->GetWidget()->GetNativeWindow())->alignment();
+ icon_layer_->SetOpacity(0);
+ background_layer_->SetOpacity(0);
+ if (!show_icon_)
+ return;
// Setup icon initial state.
- icon_layer_->SetOpacity(0);
transform.MakeIdentity();
-
transform.Translate(center.x() - kIconStartSizeDip / 2.f,
center.y() - kIconStartSizeDip / 2.f);
@@ -237,18 +523,8 @@ void VoiceInteractionOverlay::StartAnimation() {
// Setup icon animation.
scale_factor = kIconSizeDip / kIconInitSizeDip;
transform.MakeIdentity();
- if (alignment == SHELF_ALIGNMENT_BOTTOM ||
- alignment == SHELF_ALIGNMENT_BOTTOM_LOCKED) {
- transform.Translate(center.x() - kIconSizeDip / 2 + kIconOffsetDip,
- center.y() - kIconSizeDip / 2 - kIconOffsetDip);
- } else if (alignment == SHELF_ALIGNMENT_RIGHT) {
- transform.Translate(center.x() - kIconSizeDip / 2 - kIconOffsetDip,
- center.y() - kIconSizeDip / 2 + kIconOffsetDip);
- } else {
- DCHECK_EQ(alignment, SHELF_ALIGNMENT_LEFT);
- transform.Translate(center.x() - kIconSizeDip / 2 + kIconOffsetDip,
- center.y() - kIconSizeDip / 2 + kIconOffsetDip);
- }
+ transform.Translate(center.x() - kIconSizeDip / 2 + kIconOffsetDip,
+ center.y() - kIconSizeDip / 2 - kIconOffsetDip);
transform.Scale(scale_factor, scale_factor);
{
@@ -262,7 +538,7 @@ void VoiceInteractionOverlay::StartAnimation() {
}
// Setup background initial state.
- background_layer_->SetOpacity(0);
+ background_layer_->ResetShape();
transform.MakeIdentity();
transform.Translate(center.x() - kBackgroundStartSizeDip / 2.f,
@@ -275,18 +551,8 @@ void VoiceInteractionOverlay::StartAnimation() {
// Setup background animation.
scale_factor = kBackgroundSizeDip / kBackgroundInitSizeDip;
transform.MakeIdentity();
- if (alignment == SHELF_ALIGNMENT_BOTTOM ||
- alignment == SHELF_ALIGNMENT_BOTTOM_LOCKED) {
- transform.Translate(center.x() - kBackgroundSizeDip / 2 + kIconOffsetDip,
- center.y() - kBackgroundSizeDip / 2 - kIconOffsetDip);
- } else if (alignment == SHELF_ALIGNMENT_RIGHT) {
- transform.Translate(center.x() - kBackgroundSizeDip / 2 - kIconOffsetDip,
- center.y() - kBackgroundSizeDip / 2 + kIconOffsetDip);
- } else {
- DCHECK_EQ(alignment, SHELF_ALIGNMENT_LEFT);
- transform.Translate(center.x() - kBackgroundSizeDip / 2 + kIconOffsetDip,
- center.y() - kBackgroundSizeDip / 2 + kIconOffsetDip);
- }
+ transform.Translate(center.x() - kBackgroundSizeDip / 2 + kIconOffsetDip,
+ center.y() - kBackgroundSizeDip / 2 - kIconOffsetDip);
transform.Scale(scale_factor, scale_factor);
{
@@ -310,46 +576,71 @@ void VoiceInteractionOverlay::StartAnimation() {
void VoiceInteractionOverlay::BurstAnimation() {
is_bursting_ = true;
+ should_hide_animation_ = false;
gfx::Point center = host_view_->GetCenterPoint();
+ gfx::Transform transform;
// Setup ripple animations.
{
SkMScalar scale_factor =
kRippleCircleBurstRadiusDip / kRippleCircleInitRadiusDip;
- std::unique_ptr<gfx::Transform> transform(new gfx::Transform());
- transform->Translate(center.x() - kRippleCircleBurstRadiusDip,
- center.y() - kRippleCircleBurstRadiusDip);
- transform->Scale(scale_factor, scale_factor);
+ transform.Translate(center.x() - kRippleCircleBurstRadiusDip,
+ center.y() - kRippleCircleBurstRadiusDip);
+ transform.Scale(scale_factor, scale_factor);
ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kFullBurstDurationMs));
settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
+ settings.SetPreemptionStrategy(
+ ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
- ripple_layer_->SetTransform(*transform.get());
+ ripple_layer_->SetTransform(transform);
ripple_layer_->SetOpacity(0);
}
+ if (!show_icon_)
+ return;
+
// Setup icon animation.
{
ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
settings.SetTransitionDuration(
- base::TimeDelta::FromMilliseconds(kIconBurstDurationMs));
+ base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
+ settings.SetPreemptionStrategy(
+ ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
- icon_layer_->SetOpacity(0);
+ transform.MakeIdentity();
+ transform.Translate(kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip -
+ kIconEndSizeDip / 2,
+ -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip -
+ kIconEndSizeDip / 2);
+ SkMScalar scale_factor = kIconEndSizeDip / kIconInitSizeDip;
+ transform.Scale(scale_factor, scale_factor);
+
+ icon_layer_->SetTransform(transform);
}
// Setup background animation.
- {
- ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
- settings.SetTransitionDuration(
- base::TimeDelta::FromMilliseconds(kIconBurstDurationMs));
- settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
-
- background_layer_->SetOpacity(0);
- }
+ ui::CallbackLayerAnimationObserver* observer =
+ new ui::CallbackLayerAnimationObserver(
+ base::Bind(&VoiceInteractionOverlay::AnimationEndedCallback,
+ base::Unretained(this)));
+ // Transform to new shape.
+ // We want to animate from the background's current position into a larger
+ // size. The animation moves the background's center point while morphing from
+ // circle to a rectangle.
+ float x_offset = center.x() - kBackgroundSizeDip / 2 + kIconOffsetDip;
+ float y_offset = center.y() - kBackgroundSizeDip / 2 - kIconOffsetDip;
+
+ background_layer_->AnimateToLarge(
+ gfx::PointF(
+ kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip - x_offset,
+ -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip - y_offset),
+ observer);
+ observer->SetActive();
}
void VoiceInteractionOverlay::EndAnimation() {
@@ -384,6 +675,9 @@ void VoiceInteractionOverlay::EndAnimation() {
ripple_layer_->SetOpacity(0);
}
+ if (!show_icon_)
+ return;
+
// Setup icon animation.
transform.MakeIdentity();
@@ -427,4 +721,58 @@ void VoiceInteractionOverlay::EndAnimation() {
}
}
+void VoiceInteractionOverlay::HideAnimation() {
+ is_bursting_ = false;
+
+ should_hide_animation_ = true;
oshima 2017/07/06 22:04:42 should this be inside following if, and reset afte
xc 2017/07/07 16:38:15 Done.
+ if (background_layer_->GetAnimator()->is_animating()) {
+ // Wait for current animation to finish
+ return;
+ }
+
+ // Setup ripple animations.
+ {
+ ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kHideDurationMs));
+ settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
+ settings.SetPreemptionStrategy(
+ ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
+
+ ripple_layer_->SetOpacity(0);
+ }
+
+ // Setup icon animation.
+ {
+ ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kHideDurationMs));
+ settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
+ settings.SetPreemptionStrategy(
+ ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
+
+ icon_layer_->SetOpacity(0);
+ }
+
+ // Setup background animation.
+ {
+ ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kHideDurationMs));
+ settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
+ settings.SetPreemptionStrategy(
+ ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
+
+ background_layer_->SetOpacity(0);
+ }
+}
+
+bool VoiceInteractionOverlay::AnimationEndedCallback(
+ const ui::CallbackLayerAnimationObserver& observer) {
+ if (should_hide_animation_)
+ HideAnimation();
+
+ return true;
+}
+
} // namespace ash
« ash/shelf/voice_interaction_overlay.h ('K') | « ash/shelf/voice_interaction_overlay.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698