Commit ed1477cd authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

Smooth out IconLabelBubbleView animations

Re-write the math in GetSizeForLabelWidth and GetWidthBetween
(nee WidthMultiplier) so that the tween is correctly calculated
for the in-out animation.

Bug: 1005837
Change-Id: I729b6bb43265fdb7cb0aab52e6ae10e3c41a0702
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1863351
Commit-Queue: Evan Stade <estade@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709112}
parent fe5375a0
...@@ -51,11 +51,6 @@ constexpr int kIconLabelBubbleSpaceBesideSeparator = 8; ...@@ -51,11 +51,6 @@ constexpr int kIconLabelBubbleSpaceBesideSeparator = 8;
constexpr int kIconLabelBubbleFadeInDurationMs = 250; constexpr int kIconLabelBubbleFadeInDurationMs = 250;
constexpr int kIconLabelBubbleFadeOutDurationMs = 175; constexpr int kIconLabelBubbleFadeOutDurationMs = 175;
// The type of tweening for the animation.
const gfx::Tween::Type kIconLabelBubbleTweenType = gfx::Tween::EASE_IN_OUT;
// The ratio of text animation duration to total animation duration.
const double kIconLabelBubbleOpenTimeFraction = 0.2;
} // namespace } // namespace
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
...@@ -210,23 +205,32 @@ bool IconLabelBubbleView::ShouldShowSeparator() const { ...@@ -210,23 +205,32 @@ bool IconLabelBubbleView::ShouldShowSeparator() const {
return ShouldShowLabel(); return ShouldShowLabel();
} }
double IconLabelBubbleView::WidthMultiplier() const { int IconLabelBubbleView::GetWidthBetween(int min, int max) const {
// TODO(https://crbug.com/8944): Disable animations globally instead of having // TODO(https://crbug.com/8944): Disable animations globally instead of having
// piecemeal opt ins for respecting prefers reduced motion. // piecemeal opt ins for respecting prefers reduced motion.
if (gfx::Animation::PrefersReducedMotion()) if (gfx::Animation::PrefersReducedMotion())
return 1.0; return max;
if (!slide_animation_.is_animating() && !is_animation_paused_) if (!slide_animation_.is_animating() && !is_animation_paused_)
return 1.0; return max;
double progress = is_animation_paused_ ? pause_animation_state_
: slide_animation_.GetCurrentValue();
// This tween matches the default for SlideAnimation.
const gfx::Tween::Type kTween = gfx::Tween::EASE_OUT;
if (progress < open_state_fraction_) {
double state =
gfx::Tween::CalculateValue(kTween, progress / open_state_fraction_);
return gfx::Tween::IntValueBetween(state, min, max);
}
if (progress <= (1 - open_state_fraction_))
return max;
double state = is_animation_paused_ ? pause_animation_state_ double state = gfx::Tween::CalculateValue(
: slide_animation_.GetCurrentValue(); kTween, (progress - (1 - open_state_fraction_)) / open_state_fraction_);
double size_fraction = 1.0; // Note |min| and |max| are reversed.
if (state < open_state_fraction_) return gfx::Tween::IntValueBetween(state, max, min);
size_fraction = state / open_state_fraction_;
else if (state > (1.0 - open_state_fraction_))
size_fraction = (1.0 - state) / open_state_fraction_;
return size_fraction;
} }
bool IconLabelBubbleView::IsShrinking() const { bool IconLabelBubbleView::IsShrinking() const {
...@@ -246,7 +250,6 @@ bool IconLabelBubbleView::IsBubbleShowing() const { ...@@ -246,7 +250,6 @@ bool IconLabelBubbleView::IsBubbleShowing() const {
} }
gfx::Size IconLabelBubbleView::CalculatePreferredSize() const { gfx::Size IconLabelBubbleView::CalculatePreferredSize() const {
// Height will be ignored by the LocationBarView.
return GetSizeForLabelWidth(label()->GetPreferredSize().width()); return GetSizeForLabelWidth(label()->GetPreferredSize().width());
} }
...@@ -394,32 +397,25 @@ void IconLabelBubbleView::OnTouchUiChanged() { ...@@ -394,32 +397,25 @@ void IconLabelBubbleView::OnTouchUiChanged() {
} }
gfx::Size IconLabelBubbleView::GetSizeForLabelWidth(int label_width) const { gfx::Size IconLabelBubbleView::GetSizeForLabelWidth(int label_width) const {
gfx::Size size(image()->GetPreferredSize()); gfx::Size image_size = image()->GetPreferredSize();
size.Enlarge(GetInsets().left() + GetWidthBetweenIconAndSeparator() + image_size.Enlarge(GetInsets().left() + GetWidthBetweenIconAndSeparator() +
GetEndPaddingWithSeparator(), GetEndPaddingWithSeparator(),
GetInsets().height()); GetInsets().height());
const bool shrinking = IsShrinking(); const bool shrinking = IsShrinking();
// Animation continues for the last few pixels even after the label is not // The out portion of the in-out animation continues for the last few pixels
// visible in order to slide the icon into its final position. Therefore it // even after the label is not visible in order to slide the icon into its
// is necessary to animate |total_width| even when the background is hidden // final position. Therefore it is necessary to calculate additional width
// as long as the animation is still shrinking. // even when the label is hidden as long as the animation is still shrinking.
if (ShouldShowLabel() || shrinking) { if (!ShouldShowLabel() && !shrinking)
// |multiplier| grows from zero to one, stays equal to one and then shrinks return image_size;
// to zero again. The view width should correspondingly grow from the
// original width before animation started to fully showing both label and const int min_width =
// icon, stay there, then shrink to just large enough to show the icon. We shrinking ? image_size.width() : grow_animation_starting_width_;
// don't want to shrink all the way back to zero, since this would mean the const int max_width = image_size.width() + GetInternalSpacing() + label_width;
// view would completely disappear and then pop back to an icon after the
// animation finishes. // Height is ignored.
const int max_width = size.width() + GetInternalSpacing() + label_width; return gfx::Size(GetWidthBetween(min_width, max_width), 1);
const int current_width = WidthMultiplier() * max_width;
size.set_width(
shrinking ? std::max(current_width, size.width())
: std::min(current_width + grow_animation_starting_width_,
max_width));
}
return size;
} }
int IconLabelBubbleView::GetInternalSpacing() const { int IconLabelBubbleView::GetInternalSpacing() const {
...@@ -454,7 +450,6 @@ void IconLabelBubbleView::SetUpForAnimation() { ...@@ -454,7 +450,6 @@ void IconLabelBubbleView::SetUpForAnimation() {
label()->SetElideBehavior(gfx::NO_ELIDE); label()->SetElideBehavior(gfx::NO_ELIDE);
label()->SetVisible(false); label()->SetVisible(false);
slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(150)); slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(150));
slide_animation_.SetTweenType(kIconLabelBubbleTweenType);
open_state_fraction_ = 1.0; open_state_fraction_ = 1.0;
} }
...@@ -465,8 +460,9 @@ void IconLabelBubbleView::SetUpForInOutAnimation() { ...@@ -465,8 +460,9 @@ void IconLabelBubbleView::SetUpForInOutAnimation() {
// proportion of time spent in each portion of the animation is controlled by // proportion of time spent in each portion of the animation is controlled by
// kIconLabelBubbleOpenTimeFraction. // kIconLabelBubbleOpenTimeFraction.
slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(3000)); slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(3000));
open_state_fraction_ = gfx::Tween::CalculateValue( // The tween is calculated in GetWidthBetween().
kIconLabelBubbleTweenType, kIconLabelBubbleOpenTimeFraction); slide_animation_.SetTweenType(gfx::Tween::LINEAR);
open_state_fraction_ = 0.2;
} }
void IconLabelBubbleView::AnimateIn(base::Optional<int> string_id) { void IconLabelBubbleView::AnimateIn(base::Optional<int> string_id) {
......
...@@ -117,12 +117,12 @@ class IconLabelBubbleView : public views::InkDropObserver, ...@@ -117,12 +117,12 @@ class IconLabelBubbleView : public views::InkDropObserver,
// Returns true when the separator should be visible. // Returns true when the separator should be visible.
virtual bool ShouldShowSeparator() const; virtual bool ShouldShowSeparator() const;
// Returns a multiplier used to calculate the actual width of the view based // Gets the current width based on |slide_animation_| and given bounds.
// on its desired width. This ranges from 0 for a zero-width view to 1 for a // Virtual for testing.
// full-width view and can be used to animate the width of the view. virtual int GetWidthBetween(int min, int max) const;
virtual double WidthMultiplier() const;
// Returns true when animation is in progress and is shrinking. // Returns true when animation is in progress and is shrinking.
// Virtual for testing.
virtual bool IsShrinking() const; virtual bool IsShrinking() const;
// Returns true if a bubble was shown. // Returns true if a bubble was shown.
......
...@@ -65,9 +65,8 @@ class TestIconLabelBubbleView : public IconLabelBubbleView { ...@@ -65,9 +65,8 @@ class TestIconLabelBubbleView : public IconLabelBubbleView {
const gfx::Rect& GetLabelBounds() const { return label()->bounds(); } const gfx::Rect& GetLabelBounds() const { return label()->bounds(); }
State state() const { State state() const {
const double kOpenFraction = const double kOpenFraction = double{kOpenTimeMS} / kAnimationDurationMS;
static_cast<double>(kOpenTimeMS) / kAnimationDurationMS; double state = double{value_} / kNumberOfSteps;
double state = value_ / (double)kNumberOfSteps;
if (state < kOpenFraction) if (state < kOpenFraction)
return GROWING; return GROWING;
if (state > (1.0 - kOpenFraction)) if (state > (1.0 - kOpenFraction))
...@@ -95,17 +94,17 @@ class TestIconLabelBubbleView : public IconLabelBubbleView { ...@@ -95,17 +94,17 @@ class TestIconLabelBubbleView : public IconLabelBubbleView {
2 * GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING))); 2 * GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING)));
} }
double WidthMultiplier() const override { int GetWidthBetween(int min, int max) const override {
const double kOpenFraction = const double kOpenFraction =
static_cast<double>(kOpenTimeMS) / kAnimationDurationMS; static_cast<double>(kOpenTimeMS) / kAnimationDurationMS;
double fraction = value_ / (double)kNumberOfSteps; double fraction = static_cast<double>(value_) / kNumberOfSteps;
switch (state()) { switch (state()) {
case GROWING: case GROWING:
return fraction / kOpenFraction; return min + (max - min) * (fraction / kOpenFraction);
case STEADY: case STEADY:
return 1.0; return max;
case SHRINKING: case SHRINKING:
return (1.0 - fraction) / kOpenFraction; return min + (max - min) * ((1.0 - fraction) / kOpenFraction);
} }
NOTREACHED(); NOTREACHED();
return 1.0; return 1.0;
......
...@@ -125,7 +125,9 @@ void SharingIconView::UpdateOpacity() { ...@@ -125,7 +125,9 @@ void SharingIconView::UpdateOpacity() {
layer()->SetFillsBoundsOpaquely(false); layer()->SetFillsBoundsOpaquely(false);
} }
layer()->SetOpacity(PageActionIconView::WidthMultiplier()); int kLargeNumber = 100;
layer()->SetOpacity(GetWidthBetween(0, kLargeNumber) /
static_cast<float>(kLargeNumber));
} }
void SharingIconView::UpdateInkDrop(bool activate) { void SharingIconView::UpdateInkDrop(bool activate) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment