summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorskobes <skobes@chromium.org>2015-10-27 08:05:50 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-27 15:06:35 +0000
commit46aad144b73e893f8b08a18e689ff444c301a39f (patch)
tree184dce4489fb98965dd532a8d125124463821da3
parent0d0154221feddb459b8749f49ecac82e74922579 (diff)
downloadchromium_src-46aad144b73e893f8b08a18e689ff444c301a39f.zip
chromium_src-46aad144b73e893f8b08a18e689ff444c301a39f.tar.gz
chromium_src-46aad144b73e893f8b08a18e689ff444c301a39f.tar.bz2
Make compositor input-driven smooth scrolling smoother and snappier.
The animation time is now constant instead of proportional to the delta, which felt sluggish. The velocity matching in UpdateTarget now handles direction changes correctly, and scales the first control point on the y axis instead of rotating it, which gives a smoother velocity transition. BUG=575 CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel Review URL: https://codereview.chromium.org/1420943004 Cr-Commit-Position: refs/heads/master@{#356291}
-rw-r--r--cc/animation/animation_host.cc5
-rw-r--r--cc/animation/scroll_offset_animation_curve.cc80
-rw-r--r--cc/animation/scroll_offset_animation_curve.h8
-rw-r--r--cc/animation/scroll_offset_animation_curve_unittest.cc22
-rw-r--r--cc/trees/layer_tree_host_impl.cc5
5 files changed, 76 insertions, 44 deletions
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
index 5d8352f..a5bf5ea 100644
--- a/cc/animation/animation_host.cc
+++ b/cc/animation/animation_host.cc
@@ -45,8 +45,9 @@ class AnimationHost::ScrollOffsetAnimations : public AnimationDelegate {
const gfx::ScrollOffset& target_offset,
const gfx::ScrollOffset& current_offset) {
scoped_ptr<ScrollOffsetAnimationCurve> curve =
- ScrollOffsetAnimationCurve::Create(target_offset,
- EaseInOutTimingFunction::Create());
+ ScrollOffsetAnimationCurve::Create(
+ target_offset, EaseInOutTimingFunction::Create(),
+ ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT);
curve->SetInitialValue(current_offset);
scoped_ptr<Animation> animation = Animation::Create(
diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc
index 17dff26..4a78c78 100644
--- a/cc/animation/scroll_offset_animation_curve.cc
+++ b/cc/animation/scroll_offset_animation_curve.cc
@@ -12,6 +12,7 @@
#include "cc/base/time_util.h"
#include "ui/gfx/animation/tween.h"
+const double kConstantDuration = 12.0;
const double kDurationDivisor = 60.0;
namespace cc {
@@ -19,58 +20,64 @@ namespace cc {
namespace {
static float MaximumDimension(const gfx::Vector2dF& delta) {
- return std::max(std::abs(delta.x()), std::abs(delta.y()));
+ return std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y();
}
-static base::TimeDelta DurationFromDelta(const gfx::Vector2dF& delta) {
- // The duration of a scroll animation depends on the size of the scroll.
- // The exact relationship between the size and the duration isn't specified
- // by the CSSOM View smooth scroll spec and is instead left up to user agents
- // to decide. The calculation performed here will very likely be further
- // tweaked before the smooth scroll API ships.
- return base::TimeDelta::FromMicroseconds(
- (std::sqrt(MaximumDimension(delta)) / kDurationDivisor) *
- base::Time::kMicrosecondsPerSecond);
+static base::TimeDelta SegmentDuration(
+ const gfx::Vector2dF& delta,
+ ScrollOffsetAnimationCurve::DurationBehavior behavior) {
+ if (behavior == ScrollOffsetAnimationCurve::DurationBehavior::DELTA_BASED) {
+ // The duration of a JS scroll animation depends on the size of the scroll.
+ // The exact relationship between the size and the duration isn't specified
+ // by the CSSOM View smooth scroll spec and is instead left up to user
+ // agents to decide. The calculation performed here will very likely be
+ // further tweaked before the smooth scroll API ships.
+ return base::TimeDelta::FromMicroseconds(
+ (std::sqrt(std::abs(MaximumDimension(delta))) / kDurationDivisor) *
+ base::Time::kMicrosecondsPerSecond);
+ } else {
+ // Input-driven scroll animations use a constant duration.
+ return base::TimeDelta::FromMicroseconds(
+ (kConstantDuration / kDurationDivisor) *
+ base::Time::kMicrosecondsPerSecond);
+ }
}
static scoped_ptr<TimingFunction> EaseOutWithInitialVelocity(double velocity) {
- // Based on EaseInOutTimingFunction::Create with first control point rotated.
- if (std::abs(velocity) < 1000.0) {
- const double r2 = 0.42 * 0.42;
- const double v2 = velocity * velocity;
- const double x1 = std::sqrt(r2 / (v2 + 1));
- const double y1 = std::sqrt(r2 * v2 / (v2 + 1));
- return CubicBezierTimingFunction::Create(x1, y1, 0.58, 1);
- }
+ // Clamp velocity to a sane value.
+ velocity = std::min(std::max(velocity, -1000.0), 1000.0);
- // For large |velocity|, x1 approaches 0 and y1 approaches 0.42. To avoid the
- // risk of floating point arithmetic involving infinity and NaN, use those
- // values directly rather than computing them above.
- return CubicBezierTimingFunction::Create(0, 0.42, 0.58, 1);
+ // Based on EaseInOutTimingFunction::Create with first control point scaled.
+ const double x1 = 0.42;
+ const double y1 = velocity * x1;
+ return CubicBezierTimingFunction::Create(x1, y1, 0.58, 1);
}
} // namespace
scoped_ptr<ScrollOffsetAnimationCurve> ScrollOffsetAnimationCurve::Create(
const gfx::ScrollOffset& target_value,
- scoped_ptr<TimingFunction> timing_function) {
- return make_scoped_ptr(
- new ScrollOffsetAnimationCurve(target_value, timing_function.Pass()));
+ scoped_ptr<TimingFunction> timing_function,
+ DurationBehavior duration_behavior) {
+ return make_scoped_ptr(new ScrollOffsetAnimationCurve(
+ target_value, timing_function.Pass(), duration_behavior));
}
ScrollOffsetAnimationCurve::ScrollOffsetAnimationCurve(
const gfx::ScrollOffset& target_value,
- scoped_ptr<TimingFunction> timing_function)
- : target_value_(target_value), timing_function_(timing_function.Pass()) {
-}
+ scoped_ptr<TimingFunction> timing_function,
+ DurationBehavior duration_behavior)
+ : target_value_(target_value),
+ timing_function_(timing_function.Pass()),
+ duration_behavior_(duration_behavior) {}
ScrollOffsetAnimationCurve::~ScrollOffsetAnimationCurve() {}
void ScrollOffsetAnimationCurve::SetInitialValue(
const gfx::ScrollOffset& initial_value) {
initial_value_ = initial_value;
- total_animation_duration_ = DurationFromDelta(
- target_value_.DeltaFrom(initial_value_));
+ total_animation_duration_ = SegmentDuration(
+ target_value_.DeltaFrom(initial_value_), duration_behavior_);
}
gfx::ScrollOffset ScrollOffsetAnimationCurve::GetValue(
@@ -104,7 +111,7 @@ scoped_ptr<AnimationCurve> ScrollOffsetAnimationCurve::Clone() const {
scoped_ptr<TimingFunction> timing_function(
static_cast<TimingFunction*>(timing_function_->Clone().release()));
scoped_ptr<ScrollOffsetAnimationCurve> curve_clone =
- Create(target_value_, timing_function.Pass());
+ Create(target_value_, timing_function.Pass(), duration_behavior_);
curve_clone->initial_value_ = initial_value_;
curve_clone->total_animation_duration_ = total_animation_duration_;
curve_clone->last_retarget_ = last_retarget_;
@@ -121,7 +128,8 @@ void ScrollOffsetAnimationCurve::UpdateTarget(
double old_duration =
(total_animation_duration_ - last_retarget_).InSecondsF();
- double new_duration = DurationFromDelta(new_delta).InSecondsF();
+ double new_duration =
+ SegmentDuration(new_delta, duration_behavior_).InSecondsF();
double old_velocity = timing_function_->Velocity(
(t - last_retarget_.InSecondsF()) / old_duration);
@@ -129,9 +137,13 @@ void ScrollOffsetAnimationCurve::UpdateTarget(
// TimingFunction::Velocity gives the slope of the curve from 0 to 1.
// To match the "true" velocity in px/sec we must adjust this slope for
// differences in duration and scroll delta between old and new curves.
+ const double kEpsilon = 0.01f;
+ double new_delta_max_dimension = MaximumDimension(new_delta);
double new_velocity =
- old_velocity * (new_duration / old_duration) *
- (MaximumDimension(old_delta) / MaximumDimension(new_delta));
+ new_delta_max_dimension < kEpsilon // Guard against division by 0.
+ ? old_velocity
+ : old_velocity * (new_duration / old_duration) *
+ (MaximumDimension(old_delta) / new_delta_max_dimension);
initial_value_ = current_position;
target_value_ = new_target;
diff --git a/cc/animation/scroll_offset_animation_curve.h b/cc/animation/scroll_offset_animation_curve.h
index c4ae70b..4c43a20 100644
--- a/cc/animation/scroll_offset_animation_curve.h
+++ b/cc/animation/scroll_offset_animation_curve.h
@@ -17,9 +17,11 @@ class TimingFunction;
class CC_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve {
public:
+ enum class DurationBehavior { DELTA_BASED, CONSTANT };
static scoped_ptr<ScrollOffsetAnimationCurve> Create(
const gfx::ScrollOffset& target_value,
- scoped_ptr<TimingFunction> timing_function);
+ scoped_ptr<TimingFunction> timing_function,
+ DurationBehavior = DurationBehavior::DELTA_BASED);
~ScrollOffsetAnimationCurve() override;
@@ -35,7 +37,8 @@ class CC_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve {
private:
ScrollOffsetAnimationCurve(const gfx::ScrollOffset& target_value,
- scoped_ptr <TimingFunction> timing_function);
+ scoped_ptr<TimingFunction> timing_function,
+ DurationBehavior);
gfx::ScrollOffset initial_value_;
gfx::ScrollOffset target_value_;
@@ -45,6 +48,7 @@ class CC_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve {
base::TimeDelta last_retarget_;
scoped_ptr<TimingFunction> timing_function_;
+ DurationBehavior duration_behavior_;
DISALLOW_COPY_AND_ASSIGN(ScrollOffsetAnimationCurve);
};
diff --git a/cc/animation/scroll_offset_animation_curve_unittest.cc b/cc/animation/scroll_offset_animation_curve_unittest.cc
index 5887513..fd34db0 100644
--- a/cc/animation/scroll_offset_animation_curve_unittest.cc
+++ b/cc/animation/scroll_offset_animation_curve_unittest.cc
@@ -142,16 +142,16 @@ TEST(ScrollOffsetAnimationCurveTest, UpdateTarget) {
EXPECT_EQ(2.0, curve->Duration().InSecondsF());
EXPECT_EQ(1800.0, curve->GetValue(base::TimeDelta::FromSecondsD(0.5)).y());
- EXPECT_NEAR(5566.49, curve->GetValue(base::TimeDelta::FromSecondsD(1.0)).y(),
+ EXPECT_NEAR(5410.05, curve->GetValue(base::TimeDelta::FromSecondsD(1.0)).y(),
0.01);
EXPECT_EQ(9900.0, curve->GetValue(base::TimeDelta::FromSecondsD(2.0)).y());
curve->UpdateTarget(1.0, gfx::ScrollOffset(0.0, 7200.0));
- EXPECT_NEAR(1.674, curve->Duration().InSecondsF(), 0.01);
- EXPECT_NEAR(5566.49, curve->GetValue(base::TimeDelta::FromSecondsD(1.0)).y(),
+ EXPECT_NEAR(1.705, curve->Duration().InSecondsF(), 0.01);
+ EXPECT_NEAR(5410.05, curve->GetValue(base::TimeDelta::FromSecondsD(1.0)).y(),
0.01);
- EXPECT_EQ(7200.0, curve->GetValue(base::TimeDelta::FromSecondsD(1.674)).y());
+ EXPECT_EQ(7200.0, curve->GetValue(base::TimeDelta::FromSecondsD(1.705)).y());
}
TEST(ScrollOffsetAnimationCurveTest, UpdateTargetWithLargeVelocity) {
@@ -177,5 +177,19 @@ TEST(ScrollOffsetAnimationCurveTest, UpdateTargetWithLargeVelocity) {
0.001);
}
+TEST(ScrollOffsetAnimationCurveTest, UpdateTargetConstantDuration) {
+ gfx::ScrollOffset initial_value(0.f, 0.f);
+ gfx::ScrollOffset target_value(0.f, 3600.f);
+ scoped_ptr<ScrollOffsetAnimationCurve> curve(
+ ScrollOffsetAnimationCurve::Create(
+ target_value, EaseInOutTimingFunction::Create().Pass(),
+ ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT));
+ curve->SetInitialValue(initial_value);
+ EXPECT_EQ(0.2, curve->Duration().InSecondsF());
+
+ curve->UpdateTarget(0.1, gfx::ScrollOffset(0.0, 9900.0));
+ EXPECT_EQ(0.3, curve->Duration().InSecondsF());
+}
+
} // namespace
} // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 235817d..9f1efdc 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -3453,8 +3453,9 @@ void LayerTreeHostImpl::ScrollAnimationCreate(
layer_impl->id(), target_offset, current_offset);
scoped_ptr<ScrollOffsetAnimationCurve> curve =
- ScrollOffsetAnimationCurve::Create(target_offset,
- EaseInOutTimingFunction::Create());
+ ScrollOffsetAnimationCurve::Create(
+ target_offset, EaseInOutTimingFunction::Create(),
+ ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT);
curve->SetInitialValue(current_offset);
scoped_ptr<Animation> animation = Animation::Create(