Commit 5b53900f authored by Rahul Arakeri's avatar Rahul Arakeri Committed by Chromium LUCI CQ

Impulse fling target updates as ease in-out.

Context -
This CL is specific to Impulse-based-scrolling [1]. Design doc summary
is that if the flag is turned on, scroll animations will use a
different curve. (i.e the new curve is also Bezier based but with
different control points). The CL that added this new curve is
crrev.com/c/1901628.

Problem -
The new curve causes minor jerks for flings on non-PTP touchpads that
send WM_MOUSEWHEEL(s). The reason this happens is because the steep
initial slope of the curve.

Fix -
Start off as an impulse based curve but when subsequent GSU(s) come in,
update the curve to the old ease in/out style curve. That way,
individual mousewheel ticks, keyboard presses, scrollbar clicks will
still feel impulse based and the fling will be smooth.

Misc -
Why can’t we just fix this by changing the control points on the new
Bezier curve?
If we do this, we’d be changing the personality of the Impulse curve
itself which defeats the purpose of adding this new curve in the first
place.

[1] https://docs.google.com/document/d/1A3VmlY3ZR6UtJt3QQ5uuqaCOgPjV6vCMxvkpvPBe0g0/edit#heading=h.ji0n9h8v0xez

Bug: 1157698
Change-Id: I91b90080f6eac4c268f443daf2345716374a1927
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2553869
Commit-Queue: Rahul Arakeri <arakeri@microsoft.com>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#836344}
parent 61442423
...@@ -136,7 +136,8 @@ ScrollOffsetAnimationCurve::ScrollOffsetAnimationCurve( ...@@ -136,7 +136,8 @@ ScrollOffsetAnimationCurve::ScrollOffsetAnimationCurve(
animation_type_(animation_type), animation_type_(animation_type),
duration_behavior_(duration_behavior), duration_behavior_(duration_behavior),
has_set_initial_value_(false) { has_set_initial_value_(false) {
DCHECK_EQ((animation_type == AnimationType::kEaseInOut), DCHECK_EQ((animation_type == AnimationType::kEaseInOut ||
animation_type == AnimationType::kImpulse),
duration_behavior.has_value()); duration_behavior.has_value());
switch (animation_type) { switch (animation_type) {
case AnimationType::kEaseInOut: case AnimationType::kEaseInOut:
...@@ -162,7 +163,8 @@ ScrollOffsetAnimationCurve::ScrollOffsetAnimationCurve( ...@@ -162,7 +163,8 @@ ScrollOffsetAnimationCurve::ScrollOffsetAnimationCurve(
animation_type_(animation_type), animation_type_(animation_type),
duration_behavior_(duration_behavior), duration_behavior_(duration_behavior),
has_set_initial_value_(false) { has_set_initial_value_(false) {
DCHECK_EQ((animation_type == AnimationType::kEaseInOut), DCHECK_EQ((animation_type == AnimationType::kEaseInOut ||
animation_type == AnimationType::kImpulse),
duration_behavior.has_value()); duration_behavior.has_value());
} }
...@@ -399,11 +401,8 @@ void ScrollOffsetAnimationCurve::UpdateTarget( ...@@ -399,11 +401,8 @@ void ScrollOffsetAnimationCurve::UpdateTarget(
return; return;
} }
base::TimeDelta new_duration = const base::TimeDelta new_duration =
(animation_type_ == AnimationType::kEaseInOut) EaseInOutBoundedSegmentDuration(new_delta, t, delayed_by);
? EaseInOutBoundedSegmentDuration(new_delta, t, delayed_by)
: ImpulseSegmentDuration(new_delta, delayed_by);
if (new_duration.InSecondsF() < kEpsilon) { if (new_duration.InSecondsF() < kEpsilon) {
// The duration is (close to) 0, so stop the animation. // The duration is (close to) 0, so stop the animation.
target_value_ = new_target; target_value_ = new_target;
...@@ -417,19 +416,17 @@ void ScrollOffsetAnimationCurve::UpdateTarget( ...@@ -417,19 +416,17 @@ void ScrollOffsetAnimationCurve::UpdateTarget(
double new_slope = double new_slope =
velocity * (new_duration.InSecondsF() / MaximumDimension(new_delta)); velocity * (new_duration.InSecondsF() / MaximumDimension(new_delta));
if (animation_type_ == AnimationType::kEaseInOut) { DCHECK(animation_type_ == AnimationType::kImpulse ||
timing_function_ = EaseInOutWithInitialSlope(new_slope); animation_type_ == AnimationType::kEaseInOut);
} else { if (animation_type_ == AnimationType::kImpulse &&
DCHECK_EQ(animation_type_, AnimationType::kImpulse); IsNewTargetInOppositeDirection(current_position, target_value_,
if (IsNewTargetInOppositeDirection(current_position, target_value_, new_target)) {
new_target)) { // Prevent any rubber-banding by setting the velocity (and subsequently, the
// Prevent any rubber-banding by setting the velocity (and subsequently, // slope) to 0 when moving in the opposite direciton.
// the slope) to 0 when moving in the opposite direciton. new_slope = 0;
new_slope = 0;
}
timing_function_ = ImpulseCurveWithInitialSlope(new_slope);
} }
timing_function_ = EaseInOutWithInitialSlope(new_slope);
initial_value_ = current_position; initial_value_ = current_position;
target_value_ = new_target; target_value_ = new_target;
total_animation_duration_ = t + new_duration; total_animation_duration_ = t + new_duration;
......
...@@ -92,6 +92,9 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { ...@@ -92,6 +92,9 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve {
static void SetAnimationDurationForTesting(base::TimeDelta duration); static void SetAnimationDurationForTesting(base::TimeDelta duration);
private: private:
FRIEND_TEST_ALL_PREFIXES(ScrollOffsetAnimationCurveTest, ImpulseUpdateTarget);
FRIEND_TEST_ALL_PREFIXES(ScrollOffsetAnimationCurveTest,
ImpulseUpdateTargetSwitchDirections);
friend class ScrollOffsetAnimationCurveFactory; friend class ScrollOffsetAnimationCurveFactory;
enum class AnimationType { kLinear, kEaseInOut, kImpulse }; enum class AnimationType { kLinear, kEaseInOut, kImpulse };
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "cc/animation/scroll_offset_animation_curve_factory.h" #include "cc/animation/scroll_offset_animation_curve_factory.h"
#include <memory>
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "cc/animation/timing_function.h" #include "cc/animation/timing_function.h"
#include "cc/base/features.h" #include "cc/base/features.h"
...@@ -86,6 +87,7 @@ std::unique_ptr<ScrollOffsetAnimationCurve> ...@@ -86,6 +87,7 @@ std::unique_ptr<ScrollOffsetAnimationCurve>
ScrollOffsetAnimationCurveFactory::CreateImpulseAnimation( ScrollOffsetAnimationCurveFactory::CreateImpulseAnimation(
const gfx::ScrollOffset& target_value) { const gfx::ScrollOffset& target_value) {
return base::WrapUnique(new ScrollOffsetAnimationCurve( return base::WrapUnique(new ScrollOffsetAnimationCurve(
target_value, ScrollOffsetAnimationCurve::AnimationType::kImpulse)); target_value, ScrollOffsetAnimationCurve::AnimationType::kImpulse,
ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA));
} }
} // namespace cc } // namespace cc
...@@ -21,6 +21,8 @@ namespace { ...@@ -21,6 +21,8 @@ namespace {
// This is the value of the default Impulse bezier curve when t = 0.5 // This is the value of the default Impulse bezier curve when t = 0.5
constexpr double halfway_through_default_impulse_curve = 0.874246; constexpr double halfway_through_default_impulse_curve = 0.874246;
} // namespace
TEST(ScrollOffsetAnimationCurveTest, DeltaBasedDuration) { TEST(ScrollOffsetAnimationCurveTest, DeltaBasedDuration) {
gfx::ScrollOffset target_value(100.f, 200.f); gfx::ScrollOffset target_value(100.f, 200.f);
std::unique_ptr<ScrollOffsetAnimationCurve> curve( std::unique_ptr<ScrollOffsetAnimationCurve> curve(
...@@ -208,8 +210,8 @@ TEST(ScrollOffsetAnimationCurveTest, ImpulseUpdateTarget) { ...@@ -208,8 +210,8 @@ TEST(ScrollOffsetAnimationCurveTest, ImpulseUpdateTarget) {
gfx::Vector2dF new_delta = gfx::Vector2dF new_delta =
new_target_value.DeltaFrom(distance_halfway_through_initial_animation); new_target_value.DeltaFrom(distance_halfway_through_initial_animation);
base::TimeDelta updated_segment_duration = base::TimeDelta updated_segment_duration =
ScrollOffsetAnimationCurve::ImpulseSegmentDuration(new_delta, curve->EaseInOutBoundedSegmentDuration(new_delta, base::TimeDelta(),
base::TimeDelta()); base::TimeDelta());
base::TimeDelta overall_duration = time_of_update + updated_segment_duration; base::TimeDelta overall_duration = time_of_update + updated_segment_duration;
EXPECT_NEAR(overall_duration.InSecondsF(), curve->Duration().InSecondsF(), EXPECT_NEAR(overall_duration.InSecondsF(), curve->Duration().InSecondsF(),
...@@ -257,25 +259,26 @@ TEST(ScrollOffsetAnimationCurveTest, ImpulseUpdateTargetSwitchDirections) { ...@@ -257,25 +259,26 @@ TEST(ScrollOffsetAnimationCurveTest, ImpulseUpdateTargetSwitchDirections) {
curve->UpdateTarget(base::TimeDelta::FromSecondsD(initial_duration / 2), curve->UpdateTarget(base::TimeDelta::FromSecondsD(initial_duration / 2),
updated_target); updated_target);
double updated_duration =
ScrollOffsetAnimationCurve::ImpulseSegmentDuration(
gfx::Vector2dF(updated_initial_value.x(), updated_initial_value.y()),
base::TimeDelta())
.InSecondsF();
EXPECT_NEAR( EXPECT_NEAR(
initial_target_value.y() * halfway_through_default_impulse_curve, initial_target_value.y() * halfway_through_default_impulse_curve,
curve->GetValue(base::TimeDelta::FromSecondsD(initial_duration / 2.0)) curve->GetValue(base::TimeDelta::FromSecondsD(initial_duration / 2.0))
.y(), .y(),
0.01f); 0.01f);
EXPECT_NEAR( // Once the impulse style curve is updated, it turns to an ease-in ease-out
updated_initial_value.y() * (1.0 - halfway_through_default_impulse_curve), // type curve.
curve double updated_duration = curve
->GetValue(base::TimeDelta::FromSecondsD(initial_duration / 2.0 + ->EaseInOutBoundedSegmentDuration(
updated_duration / 2.0)) gfx::Vector2dF(updated_initial_value.x(),
.y(), updated_initial_value.y()),
0.01f); base::TimeDelta(), base::TimeDelta())
.InSecondsF();
EXPECT_NEAR(updated_initial_value.y() * 0.5,
curve
->GetValue(base::TimeDelta::FromSecondsD(
initial_duration / 2.0 + updated_duration / 2.0))
.y(),
0.01f);
EXPECT_NEAR(0.0, EXPECT_NEAR(0.0,
curve curve
->GetValue(base::TimeDelta::FromSecondsD( ->GetValue(base::TimeDelta::FromSecondsD(
...@@ -453,5 +456,4 @@ TEST(ScrollOffsetAnimationCurveTest, UpdateTargetZeroLastSegmentDuration) { ...@@ -453,5 +456,4 @@ TEST(ScrollOffsetAnimationCurveTest, UpdateTargetZeroLastSegmentDuration) {
EXPECT_NEAR(expected_duration, curve->Duration().InSecondsF(), 0.0002f); EXPECT_NEAR(expected_duration, curve->Duration().InSecondsF(), 0.0002f);
} }
} // namespace
} // namespace cc } // namespace cc
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