Commit 5204141e authored by Rahul Arakeri's avatar Rahul Arakeri Committed by Commit Bot

Fix for stray finger movements.

When user enters into an overscroll (without fling) and lifts off the
fingers while the scroller is stretched, we can occasionally observe
forward jerks before a bounce back animation plays. This happens due to
stray finger movements. When the finger lifts off, there can be small
velocities that trigger a forward animation (i.e the quick forward
jerk). This CL fixes the issue by preventing the scroller from playing
the bounce forward animation when velocities are really small.

Bug: 1116057
Change-Id: I1f3c53b510172b6d5509e09354db3d97a9814492
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2348009Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Commit-Queue: Rahul Arakeri <arakeri@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#797767}
parent b9a67613
......@@ -110,6 +110,8 @@ class PLATFORM_EXPORT ElasticOverscrollController {
VerifyBackwardAnimationTick);
FRIEND_TEST_ALL_PREFIXES(ElasticOverscrollControllerBezierTest,
VerifyForwardAnimationTick);
FRIEND_TEST_ALL_PREFIXES(ElasticOverscrollControllerBezierTest,
VerifyForwardAnimationIsNotPlayed);
enum State {
// The initial state, during which the overscroll amount is zero and
......
......@@ -19,10 +19,15 @@ constexpr double kBounceBackMaxDurationMilliseconds = 300.0;
// Time taken by the bounce back animation (in milliseconds) to scroll 1 px.
constexpr double kBounceBackMillisecondsPerPixel = 15.0;
const double kOverbounceMaxDurationMilliseconds = 150.0;
const double kOverbounceMillisecondsPerPixel = 2.5;
// Threshold above which a forward animation should be played. Stray finger
// movements can cause velocities to be non-zero. This in-turn may lead to minor
// jerks when the bounce back animation is being played. Expressed in pixels per
// second.
constexpr double kIgnoreForwardBounceVelocityThreshold = 200;
const float kOverbounceDistanceMultiplier = 35.f;
constexpr double kOverbounceMaxDurationMilliseconds = 150.0;
constexpr double kOverbounceMillisecondsPerPixel = 2.5;
constexpr double kOverbounceDistanceMultiplier = 35.f;
// Control points for the bounce forward Cubic Bezier curve.
constexpr double kBounceForwardsX1 = 0.25;
......@@ -72,8 +77,13 @@ gfx::Vector2dF ElasticOverscrollControllerBezier::OverscrollBoundary(
void ElasticOverscrollControllerBezier::DidEnterMomentumAnimatedState() {
// Express velocity in terms of milliseconds.
const gfx::Vector2dF velocity(scroll_velocity().x() / 1000.f,
scroll_velocity().y() / 1000.f);
const gfx::Vector2dF velocity(
fabs(scroll_velocity().x()) > kIgnoreForwardBounceVelocityThreshold
? scroll_velocity().x() / 1000.f
: 0.f,
fabs(scroll_velocity().y()) > kIgnoreForwardBounceVelocityThreshold
? scroll_velocity().y() / 1000.f
: 0.f);
gfx::Vector2dF bounce_forwards_delta(gfx::Vector2dF(
sqrt(std::abs(velocity.x())), sqrt(std::abs(velocity.y()))));
......@@ -102,6 +112,8 @@ void ElasticOverscrollControllerBezier::DidEnterMomentumAnimatedState() {
? std::copysign(bounce_forwards_distance_.y(), velocity.y())
: std::copysign(bounce_forwards_distance_.y(),
momentum_animation_initial_stretch_.y()));
bounce_forwards_duration_x_ =
CalculateBounceForwardsDuration(bounce_forwards_delta.x());
bounce_forwards_duration_y_ =
CalculateBounceForwardsDuration(bounce_forwards_delta.y());
......
......@@ -45,6 +45,9 @@ class PLATFORM_EXPORT ElasticOverscrollControllerBezier
const double bounce_forwards_distance) const;
private:
FRIEND_TEST_ALL_PREFIXES(ElasticOverscrollControllerBezierTest,
VerifyForwardAnimationIsNotPlayed);
const gfx::CubicBezier bounce_forwards_curve_;
base::TimeDelta bounce_forwards_duration_x_;
base::TimeDelta bounce_forwards_duration_y_;
......
......@@ -8,6 +8,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
namespace blink {
......@@ -210,29 +211,75 @@ TEST_F(ElasticOverscrollControllerBezierTest, VerifyBackwardAnimationTick) {
// Tests that the bounce forward animation ticks as expected.
TEST_F(ElasticOverscrollControllerBezierTest, VerifyForwardAnimationTick) {
// Test vertical forward bounce animations.
EXPECT_EQ(controller_.state_, ElasticOverscrollController::kStateInactive);
SendGestureScrollBegin(PhaseState::kNonMomentum);
EXPECT_EQ(Vector2dF(0, 0), helper_.StretchAmount());
SendGestureScrollUpdate(PhaseState::kNonMomentum, Vector2dF(0, -100));
controller_.scroll_velocity_ = gfx::Vector2dF(0.f, -500.f);
controller_.scroll_velocity_ = gfx::Vector2dF(0.f, -4000.f);
// This signals that the finger has lifted off which triggers a fling.
const base::TimeTicks now = base::TimeTicks::Now();
SendGestureScrollEnd(now);
const int SAMPLES = 7;
const int frames[SAMPLES] = {1, 2, 3, 4, 5, 8, 19};
const int stretch_amount[SAMPLES] = {-33, -40, -43, -40, -26, -11, 0};
const int TOTAL_FRAMES = 26;
const int stretch_amount_y[TOTAL_FRAMES] = {
-19, -41, -55, -65, -72, -78, -82, -85, -88, -89, -64, -45, -34,
-26, -20, -16, -13, -10, -8, -6, -4, -3, -2, -1, -1, 0};
for (int i = 0; i < SAMPLES; i++) {
controller_.Animate(now +
base::TimeDelta::FromMilliseconds(frames[i] * 16));
for (int i = 0; i < TOTAL_FRAMES; i++) {
controller_.Animate(now + base::TimeDelta::FromMilliseconds(i * 16));
EXPECT_EQ(controller_.state_,
(stretch_amount[i] == 0
(stretch_amount_y[i] == 0
? ElasticOverscrollController::kStateInactive
: ElasticOverscrollController::kStateMomentumAnimated));
ASSERT_FLOAT_EQ(helper_.StretchAmount().y(), stretch_amount[i]);
ASSERT_FLOAT_EQ(helper_.StretchAmount().y(), stretch_amount_y[i]);
}
// Test horizontal forward bounce animations.
SendGestureScrollBegin(PhaseState::kNonMomentum);
SendGestureScrollUpdate(PhaseState::kNonMomentum, Vector2dF(-50, 0));
controller_.scroll_velocity_ = gfx::Vector2dF(-3000.f, 0.f);
SendGestureScrollEnd(now);
const int stretch_amount_x[TOTAL_FRAMES] = {
-9, -28, -40, -49, -55, -60, -64, -66, -68, -69, -50, -35, -26,
-20, -16, -12, -10, -8, -6, -5, -3, -2, -2, -1, -1, 0};
for (int i = 0; i < TOTAL_FRAMES; i++) {
controller_.Animate(now + base::TimeDelta::FromMilliseconds(i * 16));
EXPECT_EQ(controller_.state_,
(stretch_amount_x[i] == 0
? ElasticOverscrollController::kStateInactive
: ElasticOverscrollController::kStateMomentumAnimated));
ASSERT_FLOAT_EQ(helper_.StretchAmount().x(), stretch_amount_x[i]);
}
}
// Tests that the bounce forward animation is *not* played when the velocity is
// less than kIgnoreForwardBounceVelocityThreshold. This can be verified by
// checking bounce_forwards_distance_ (since it is a function of velocity)
TEST_F(ElasticOverscrollControllerBezierTest,
VerifyForwardAnimationIsNotPlayed) {
EXPECT_EQ(Vector2dF(), helper_.StretchAmount());
controller_.scroll_velocity_ = gfx::Vector2dF(0.f, -199.f);
controller_.DidEnterMomentumAnimatedState();
EXPECT_TRUE(controller_.bounce_forwards_distance_.IsZero());
controller_.scroll_velocity_ = gfx::Vector2dF(-199.f, 0.f);
controller_.DidEnterMomentumAnimatedState();
EXPECT_TRUE(controller_.bounce_forwards_distance_.IsZero());
// When velocity > 200, forward animation is expected to be played.
controller_.scroll_velocity_ = gfx::Vector2dF(0.f, -201.f);
controller_.DidEnterMomentumAnimatedState();
EXPECT_EQ(gfx::Vector2dF(0, -16),
gfx::ToRoundedVector2d(controller_.bounce_forwards_distance_));
controller_.scroll_velocity_ = gfx::Vector2dF(-201.f, 0.f);
controller_.DidEnterMomentumAnimatedState();
EXPECT_EQ(gfx::Vector2dF(-16, 0),
gfx::ToRoundedVector2d(controller_.bounce_forwards_distance_));
}
// Tests initiating a scroll when a bounce back animation is in progress works
......
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