diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc index c37506e5489806..0c684c019953dc 100644 --- a/cc/animation/animation_host.cc +++ b/cc/animation/animation_host.cc @@ -48,7 +48,7 @@ class AnimationHost::ScrollOffsetAnimations : public AnimationDelegate { scoped_ptr curve = ScrollOffsetAnimationCurve::Create( target_offset, EaseInOutTimingFunction::Create(), - ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT); + ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA); curve->SetInitialValue(current_offset); scoped_ptr animation = Animation::Create( diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc index 371f4f6d6c9d68..1f44bbadadcdfe 100644 --- a/cc/animation/scroll_offset_animation_curve.cc +++ b/cc/animation/scroll_offset_animation_curve.cc @@ -12,9 +12,23 @@ #include "cc/base/time_util.h" #include "ui/gfx/animation/tween.h" -const double kConstantDuration = 12.0; +using DurationBehavior = cc::ScrollOffsetAnimationCurve::DurationBehavior; + +const double kConstantDuration = 9.0; const double kDurationDivisor = 60.0; +const double kInverseDeltaRampStartPx = 120.0; +const double kInverseDeltaRampEndPx = 480.0; +const double kInverseDeltaMinDuration = 6.0; +const double kInverseDeltaMaxDuration = 12.0; + +const double kInverseDeltaSlope = + (kInverseDeltaMinDuration - kInverseDeltaMaxDuration) / + (kInverseDeltaRampEndPx - kInverseDeltaRampStartPx); + +const double kInverseDeltaOffset = + kInverseDeltaMaxDuration - kInverseDeltaRampStartPx * kInverseDeltaSlope; + namespace cc { namespace { @@ -25,24 +39,28 @@ static float MaximumDimension(const gfx::Vector2dF& delta) { return std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y(); } -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 base::TimeDelta SegmentDuration(const gfx::Vector2dF& delta, + DurationBehavior behavior) { + double duration = kConstantDuration; + switch (behavior) { + case DurationBehavior::CONSTANT: + duration = kConstantDuration; + break; + case DurationBehavior::DELTA_BASED: + duration = std::sqrt(std::abs(MaximumDimension(delta))); + break; + case DurationBehavior::INVERSE_DELTA: + duration = std::min( + std::max(kInverseDeltaOffset + + std::abs(MaximumDimension(delta)) * kInverseDeltaSlope, + kInverseDeltaMinDuration), + kInverseDeltaMaxDuration); + break; + default: + NOTREACHED(); } + return base::TimeDelta::FromMicroseconds(duration / kDurationDivisor * + base::Time::kMicrosecondsPerSecond); } static scoped_ptr EaseOutWithInitialVelocity(double velocity) { diff --git a/cc/animation/scroll_offset_animation_curve.h b/cc/animation/scroll_offset_animation_curve.h index d27a6dd17259d2..b874f119ccda65 100644 --- a/cc/animation/scroll_offset_animation_curve.h +++ b/cc/animation/scroll_offset_animation_curve.h @@ -18,7 +18,7 @@ class TimingFunction; class CC_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { public: - enum class DurationBehavior { DELTA_BASED, CONSTANT }; + enum class DurationBehavior { DELTA_BASED, CONSTANT, INVERSE_DELTA }; static scoped_ptr Create( const gfx::ScrollOffset& target_value, scoped_ptr timing_function, diff --git a/cc/animation/scroll_offset_animation_curve_unittest.cc b/cc/animation/scroll_offset_animation_curve_unittest.cc index 7044b03addae2c..1f6e62a2bf3d98 100644 --- a/cc/animation/scroll_offset_animation_curve_unittest.cc +++ b/cc/animation/scroll_offset_animation_curve_unittest.cc @@ -9,10 +9,13 @@ #include "cc/test/geometry_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +const double kConstantDuration = 9.0; +const double kDurationDivisor = 60.0; + namespace cc { namespace { -TEST(ScrollOffsetAnimationCurveTest, Duration) { +TEST(ScrollOffsetAnimationCurveTest, DeltaBasedDuration) { gfx::ScrollOffset target_value(100.f, 200.f); scoped_ptr curve( ScrollOffsetAnimationCurve::Create(target_value, @@ -127,30 +130,69 @@ TEST(ScrollOffsetAnimationCurveTest, Clone) { TEST(ScrollOffsetAnimationCurveTest, UpdateTarget) { gfx::ScrollOffset initial_value(0.f, 0.f); gfx::ScrollOffset target_value(0.f, 3600.f); + double duration = kConstantDuration / kDurationDivisor; scoped_ptr curve( ScrollOffsetAnimationCurve::Create( target_value, EaseInOutTimingFunction::Create(), ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT)); curve->SetInitialValue(initial_value); - EXPECT_EQ(0.2, curve->Duration().InSecondsF()); - EXPECT_EQ(1800.0, curve->GetValue(base::TimeDelta::FromSecondsD(0.1)).y()); - EXPECT_EQ(3600.0, curve->GetValue(base::TimeDelta::FromSecondsD(0.2)).y()); + EXPECT_NEAR(duration, curve->Duration().InSecondsF(), 0.0002f); + EXPECT_NEAR( + 1800.0, + curve->GetValue(base::TimeDelta::FromSecondsD(duration / 2.0)).y(), + 0.0002f); + EXPECT_NEAR(3600.0, + curve->GetValue(base::TimeDelta::FromSecondsD(duration)).y(), + 0.0002f); + + curve->UpdateTarget(duration / 2, gfx::ScrollOffset(0.0, 9900.0)); + + EXPECT_NEAR(duration * 1.5, curve->Duration().InSecondsF(), 0.0002f); + EXPECT_NEAR( + 1800.0, + curve->GetValue(base::TimeDelta::FromSecondsD(duration / 2.0)).y(), + 0.0002f); + EXPECT_NEAR(6827.6, + curve->GetValue(base::TimeDelta::FromSecondsD(duration)).y(), + 0.1f); + EXPECT_NEAR( + 9900.0, + curve->GetValue(base::TimeDelta::FromSecondsD(duration * 1.5)).y(), + 0.0002f); + + curve->UpdateTarget(duration, gfx::ScrollOffset(0.0, 7200.0)); - curve->UpdateTarget(0.1, gfx::ScrollOffset(0.0, 9900.0)); + // A closer target at high velocity reduces the duration. + EXPECT_NEAR(duration * 1.0794, curve->Duration().InSecondsF(), 0.0002f); + EXPECT_NEAR(6827.6, + curve->GetValue(base::TimeDelta::FromSecondsD(duration)).y(), + 0.1f); + EXPECT_NEAR( + 7200.0, + curve->GetValue(base::TimeDelta::FromSecondsD(duration * 1.08)).y(), + 0.0002f); +} - EXPECT_EQ(0.3, curve->Duration().InSecondsF()); - EXPECT_EQ(1800.0, curve->GetValue(base::TimeDelta::FromSecondsD(0.1)).y()); - EXPECT_NEAR(6827.59, curve->GetValue(base::TimeDelta::FromSecondsD(0.2)).y(), - 0.01); - EXPECT_EQ(9900.0, curve->GetValue(base::TimeDelta::FromSecondsD(0.3)).y()); +TEST(ScrollOffsetAnimationCurveTest, InverseDeltaDuration) { + scoped_ptr curve( + ScrollOffsetAnimationCurve::Create( + gfx::ScrollOffset(0.f, 100.f), EaseInOutTimingFunction::Create(), + ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA)); - curve->UpdateTarget(0.2, gfx::ScrollOffset(0.0, 7200.0)); + curve->SetInitialValue(gfx::ScrollOffset()); + double smallDeltaDuration = curve->Duration().InSecondsF(); - // A closer target at high velocity reduces the duration. - EXPECT_NEAR(0.22, curve->Duration().InSecondsF(), 0.01); - EXPECT_NEAR(6827.59, curve->GetValue(base::TimeDelta::FromSecondsD(0.2)).y(), - 0.01); - EXPECT_EQ(7200.0, curve->GetValue(base::TimeDelta::FromSecondsD(0.22)).y()); + curve->UpdateTarget(0.f, gfx::ScrollOffset(0.f, 300.f)); + double mediumDeltaDuration = curve->Duration().InSecondsF(); + + curve->UpdateTarget(0.f, gfx::ScrollOffset(0.f, 500.f)); + double largeDeltaDuration = curve->Duration().InSecondsF(); + + EXPECT_GT(smallDeltaDuration, mediumDeltaDuration); + EXPECT_GT(mediumDeltaDuration, largeDeltaDuration); + + curve->UpdateTarget(0.f, gfx::ScrollOffset(0.f, 5000.f)); + EXPECT_EQ(largeDeltaDuration, curve->Duration().InSecondsF()); } } // namespace diff --git a/cc/blink/web_scroll_offset_animation_curve_impl.cc b/cc/blink/web_scroll_offset_animation_curve_impl.cc index d9c535ba4e0a04..b2c72ba8855391 100644 --- a/cc/blink/web_scroll_offset_animation_curve_impl.cc +++ b/cc/blink/web_scroll_offset_animation_curve_impl.cc @@ -22,6 +22,9 @@ static DurationBehavior GetDurationBehavior( case WebScrollOffsetAnimationCurve::ScrollDurationConstant: return DurationBehavior::CONSTANT; + + case WebScrollOffsetAnimationCurve::ScrollDurationInverseDelta: + return DurationBehavior::INVERSE_DELTA; } NOTREACHED(); return DurationBehavior::DELTA_BASED; diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index bc5a93e63b8a6e..0d6ba6d7f85c58 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -3631,7 +3631,7 @@ void LayerTreeHostImpl::ScrollAnimationCreate( scoped_ptr curve = ScrollOffsetAnimationCurve::Create( target_offset, EaseInOutTimingFunction::Create(), - ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT); + ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA); curve->SetInitialValue(current_offset); scoped_ptr animation = Animation::Create( diff --git a/third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp b/third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp index fb20c05ace3563..68418361202cd2 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp +++ b/third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp @@ -51,6 +51,7 @@ PassOwnPtrWillBeRawPtr ScrollAnimatorBase::create(Scrollable ScrollAnimator::ScrollAnimator(ScrollableArea* scrollableArea, WTF::TimeFunction timeFunction) : ScrollAnimatorBase(scrollableArea) , m_timeFunction(timeFunction) + , m_lastGranularity(ScrollByPixel) { } @@ -143,6 +144,7 @@ ScrollResultOneDimensional ScrollAnimator::userScroll( m_targetOffset = targetPos; m_startTime = m_timeFunction(); + m_lastGranularity = granularity; if (registerAndScheduleAnimation()) m_runState = RunState::WaitingToSendToCompositor; @@ -226,7 +228,9 @@ void ScrollAnimator::updateCompositorAnimations() ->createScrollOffsetAnimationCurve( m_targetOffset, WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut, - WebScrollOffsetAnimationCurve::ScrollDurationConstant)); + m_lastGranularity == ScrollByPixel ? + WebScrollOffsetAnimationCurve::ScrollDurationInverseDelta : + WebScrollOffsetAnimationCurve::ScrollDurationConstant)); m_animationCurve->setInitialValue(currentPosition()); } diff --git a/third_party/WebKit/Source/platform/scroll/ScrollAnimator.h b/third_party/WebKit/Source/platform/scroll/ScrollAnimator.h index 5c38baf25e1482..e8ac2aea1384b5 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollAnimator.h +++ b/third_party/WebKit/Source/platform/scroll/ScrollAnimator.h @@ -78,6 +78,7 @@ class PLATFORM_EXPORT ScrollAnimator final : public ScrollAnimatorBase { bool registerAndScheduleAnimation(); FloatPoint m_targetOffset; + ScrollGranularity m_lastGranularity; }; } // namespace blink diff --git a/third_party/WebKit/public/platform/WebScrollOffsetAnimationCurve.h b/third_party/WebKit/public/platform/WebScrollOffsetAnimationCurve.h index a3d0b48d9c3675..91ad7d739f18c8 100644 --- a/third_party/WebKit/public/platform/WebScrollOffsetAnimationCurve.h +++ b/third_party/WebKit/public/platform/WebScrollOffsetAnimationCurve.h @@ -16,7 +16,8 @@ class WebScrollOffsetAnimationCurve : public WebCompositorAnimationCurve { enum ScrollDurationBehavior { ScrollDurationDeltaBased = 0, - ScrollDurationConstant + ScrollDurationConstant, + ScrollDurationInverseDelta }; virtual void setInitialValue(WebFloatPoint) = 0;