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;
constexpr int kIconLabelBubbleFadeInDurationMs = 250;
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
//////////////////////////////////////////////////////////////////
......@@ -210,23 +205,32 @@ bool IconLabelBubbleView::ShouldShowSeparator() const {
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
// piecemeal opt ins for respecting prefers reduced motion.
if (gfx::Animation::PrefersReducedMotion())
return 1.0;
return max;
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_
: slide_animation_.GetCurrentValue();
double size_fraction = 1.0;
if (state < open_state_fraction_)
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;
double state = gfx::Tween::CalculateValue(
kTween, (progress - (1 - open_state_fraction_)) / open_state_fraction_);
// Note |min| and |max| are reversed.
return gfx::Tween::IntValueBetween(state, max, min);
}
bool IconLabelBubbleView::IsShrinking() const {
......@@ -246,7 +250,6 @@ bool IconLabelBubbleView::IsBubbleShowing() const {
}
gfx::Size IconLabelBubbleView::CalculatePreferredSize() const {
// Height will be ignored by the LocationBarView.
return GetSizeForLabelWidth(label()->GetPreferredSize().width());
}
......@@ -394,32 +397,25 @@ void IconLabelBubbleView::OnTouchUiChanged() {
}
gfx::Size IconLabelBubbleView::GetSizeForLabelWidth(int label_width) const {
gfx::Size size(image()->GetPreferredSize());
size.Enlarge(GetInsets().left() + GetWidthBetweenIconAndSeparator() +
GetEndPaddingWithSeparator(),
GetInsets().height());
gfx::Size image_size = image()->GetPreferredSize();
image_size.Enlarge(GetInsets().left() + GetWidthBetweenIconAndSeparator() +
GetEndPaddingWithSeparator(),
GetInsets().height());
const bool shrinking = IsShrinking();
// Animation continues for the last few pixels even after the label is not
// visible in order to slide the icon into its final position. Therefore it
// is necessary to animate |total_width| even when the background is hidden
// as long as the animation is still shrinking.
if (ShouldShowLabel() || shrinking) {
// |multiplier| grows from zero to one, stays equal to one and then shrinks
// to zero again. The view width should correspondingly grow from the
// original width before animation started to fully showing both label and
// icon, stay there, then shrink to just large enough to show the icon. We
// don't want to shrink all the way back to zero, since this would mean the
// view would completely disappear and then pop back to an icon after the
// animation finishes.
const int max_width = size.width() + GetInternalSpacing() + label_width;
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;
// The out portion of the in-out animation continues for the last few pixels
// even after the label is not visible in order to slide the icon into its
// final position. Therefore it is necessary to calculate additional width
// even when the label is hidden as long as the animation is still shrinking.
if (!ShouldShowLabel() && !shrinking)
return image_size;
const int min_width =
shrinking ? image_size.width() : grow_animation_starting_width_;
const int max_width = image_size.width() + GetInternalSpacing() + label_width;
// Height is ignored.
return gfx::Size(GetWidthBetween(min_width, max_width), 1);
}
int IconLabelBubbleView::GetInternalSpacing() const {
......@@ -454,7 +450,6 @@ void IconLabelBubbleView::SetUpForAnimation() {
label()->SetElideBehavior(gfx::NO_ELIDE);
label()->SetVisible(false);
slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(150));
slide_animation_.SetTweenType(kIconLabelBubbleTweenType);
open_state_fraction_ = 1.0;
}
......@@ -465,8 +460,9 @@ void IconLabelBubbleView::SetUpForInOutAnimation() {
// proportion of time spent in each portion of the animation is controlled by
// kIconLabelBubbleOpenTimeFraction.
slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(3000));
open_state_fraction_ = gfx::Tween::CalculateValue(
kIconLabelBubbleTweenType, kIconLabelBubbleOpenTimeFraction);
// The tween is calculated in GetWidthBetween().
slide_animation_.SetTweenType(gfx::Tween::LINEAR);
open_state_fraction_ = 0.2;
}
void IconLabelBubbleView::AnimateIn(base::Optional<int> string_id) {
......
......@@ -117,12 +117,12 @@ class IconLabelBubbleView : public views::InkDropObserver,
// Returns true when the separator should be visible.
virtual bool ShouldShowSeparator() const;
// Returns a multiplier used to calculate the actual width of the view based
// on its desired width. This ranges from 0 for a zero-width view to 1 for a
// full-width view and can be used to animate the width of the view.
virtual double WidthMultiplier() const;
// Gets the current width based on |slide_animation_| and given bounds.
// Virtual for testing.
virtual int GetWidthBetween(int min, int max) const;
// Returns true when animation is in progress and is shrinking.
// Virtual for testing.
virtual bool IsShrinking() const;
// Returns true if a bubble was shown.
......
......@@ -65,9 +65,8 @@ class TestIconLabelBubbleView : public IconLabelBubbleView {
const gfx::Rect& GetLabelBounds() const { return label()->bounds(); }
State state() const {
const double kOpenFraction =
static_cast<double>(kOpenTimeMS) / kAnimationDurationMS;
double state = value_ / (double)kNumberOfSteps;
const double kOpenFraction = double{kOpenTimeMS} / kAnimationDurationMS;
double state = double{value_} / kNumberOfSteps;
if (state < kOpenFraction)
return GROWING;
if (state > (1.0 - kOpenFraction))
......@@ -95,17 +94,17 @@ class TestIconLabelBubbleView : public IconLabelBubbleView {
2 * GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING)));
}
double WidthMultiplier() const override {
int GetWidthBetween(int min, int max) const override {
const double kOpenFraction =
static_cast<double>(kOpenTimeMS) / kAnimationDurationMS;
double fraction = value_ / (double)kNumberOfSteps;
double fraction = static_cast<double>(value_) / kNumberOfSteps;
switch (state()) {
case GROWING:
return fraction / kOpenFraction;
return min + (max - min) * (fraction / kOpenFraction);
case STEADY:
return 1.0;
return max;
case SHRINKING:
return (1.0 - fraction) / kOpenFraction;
return min + (max - min) * ((1.0 - fraction) / kOpenFraction);
}
NOTREACHED();
return 1.0;
......
......@@ -125,7 +125,9 @@ void SharingIconView::UpdateOpacity() {
layer()->SetFillsBoundsOpaquely(false);
}
layer()->SetOpacity(PageActionIconView::WidthMultiplier());
int kLargeNumber = 100;
layer()->SetOpacity(GetWidthBetween(0, kLargeNumber) /
static_cast<float>(kLargeNumber));
}
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