| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/animation/scroll_offset_animation_curve.h" |
| |
| #include "cc/animation/timing_function.h" |
| #include "cc/test/geometry_test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| const double kConstantDuration = 9.0; |
| const double kDurationDivisor = 60.0; |
| const double kInverseDeltaMaxDuration = 12.0; |
| |
| namespace cc { |
| namespace { |
| |
| TEST(ScrollOffsetAnimationCurveTest, DeltaBasedDuration) { |
| gfx::ScrollOffset target_value(100.f, 200.f); |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| target_value, CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT))); |
| |
| curve->SetInitialValue(target_value); |
| EXPECT_DOUBLE_EQ(0.0, curve->Duration().InSecondsF()); |
| |
| // x decreases, y stays the same. |
| curve->SetInitialValue(gfx::ScrollOffset(136.f, 200.f)); |
| EXPECT_DOUBLE_EQ(0.1, curve->Duration().InSecondsF()); |
| |
| // x increases, y stays the same. |
| curve->SetInitialValue(gfx::ScrollOffset(19.f, 200.f)); |
| EXPECT_DOUBLE_EQ(0.15, curve->Duration().InSecondsF()); |
| |
| // x stays the same, y decreases. |
| curve->SetInitialValue(gfx::ScrollOffset(100.f, 344.f)); |
| EXPECT_DOUBLE_EQ(0.2, curve->Duration().InSecondsF()); |
| |
| // x stays the same, y increases. |
| curve->SetInitialValue(gfx::ScrollOffset(100.f, 191.f)); |
| EXPECT_DOUBLE_EQ(0.05, curve->Duration().InSecondsF()); |
| |
| // x decreases, y decreases. |
| curve->SetInitialValue(gfx::ScrollOffset(32500.f, 500.f)); |
| EXPECT_DOUBLE_EQ(3.0, curve->Duration().InSecondsF()); |
| |
| // x decreases, y increases. |
| curve->SetInitialValue(gfx::ScrollOffset(150.f, 119.f)); |
| EXPECT_DOUBLE_EQ(0.15, curve->Duration().InSecondsF()); |
| |
| // x increases, y decreases. |
| curve->SetInitialValue(gfx::ScrollOffset(0.f, 14600.f)); |
| EXPECT_DOUBLE_EQ(2.0, curve->Duration().InSecondsF()); |
| |
| // x increases, y increases. |
| curve->SetInitialValue(gfx::ScrollOffset(95.f, 191.f)); |
| EXPECT_DOUBLE_EQ(0.05, curve->Duration().InSecondsF()); |
| } |
| |
| TEST(ScrollOffsetAnimationCurveTest, GetValue) { |
| gfx::ScrollOffset initial_value(2.f, 40.f); |
| gfx::ScrollOffset target_value(10.f, 20.f); |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| target_value, CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT))); |
| curve->SetInitialValue(initial_value); |
| |
| base::TimeDelta duration = curve->Duration(); |
| EXPECT_GT(curve->Duration().InSecondsF(), 0); |
| EXPECT_LT(curve->Duration().InSecondsF(), 0.1); |
| |
| EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, curve->Type()); |
| EXPECT_EQ(duration, curve->Duration()); |
| |
| EXPECT_VECTOR2DF_EQ(initial_value, |
| curve->GetValue(base::TimeDelta::FromSecondsD(-1.0))); |
| EXPECT_VECTOR2DF_EQ(initial_value, curve->GetValue(base::TimeDelta())); |
| EXPECT_VECTOR2DF_NEAR(gfx::ScrollOffset(6.f, 30.f), |
| curve->GetValue(duration * 0.5f), 0.00025); |
| EXPECT_VECTOR2DF_EQ(target_value, curve->GetValue(duration)); |
| EXPECT_VECTOR2DF_EQ( |
| target_value, |
| curve->GetValue(duration + base::TimeDelta::FromSecondsD(1.0))); |
| |
| // Verify that GetValue takes the timing function into account. |
| gfx::ScrollOffset value = curve->GetValue(duration * 0.25f); |
| EXPECT_NEAR(3.0333f, value.x(), 0.0002f); |
| EXPECT_NEAR(37.4168f, value.y(), 0.0002f); |
| } |
| |
| // Verify that a clone behaves exactly like the original. |
| TEST(ScrollOffsetAnimationCurveTest, Clone) { |
| gfx::ScrollOffset initial_value(2.f, 40.f); |
| gfx::ScrollOffset target_value(10.f, 20.f); |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| target_value, CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT))); |
| curve->SetInitialValue(initial_value); |
| base::TimeDelta duration = curve->Duration(); |
| |
| std::unique_ptr<AnimationCurve> clone(curve->Clone()); |
| |
| EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, clone->Type()); |
| EXPECT_EQ(duration, clone->Duration()); |
| |
| EXPECT_VECTOR2DF_EQ(initial_value, |
| clone->ToScrollOffsetAnimationCurve()->GetValue( |
| base::TimeDelta::FromSecondsD(-1.0))); |
| EXPECT_VECTOR2DF_EQ( |
| initial_value, |
| clone->ToScrollOffsetAnimationCurve()->GetValue(base::TimeDelta())); |
| EXPECT_VECTOR2DF_NEAR( |
| gfx::ScrollOffset(6.f, 30.f), |
| clone->ToScrollOffsetAnimationCurve()->GetValue(duration * 0.5f), |
| 0.00025); |
| EXPECT_VECTOR2DF_EQ( |
| target_value, clone->ToScrollOffsetAnimationCurve()->GetValue(duration)); |
| EXPECT_VECTOR2DF_EQ(target_value, |
| clone->ToScrollOffsetAnimationCurve()->GetValue( |
| duration + base::TimeDelta::FromSecondsD(1.f))); |
| |
| // Verify that the timing function was cloned correctly. |
| gfx::ScrollOffset value = |
| clone->ToScrollOffsetAnimationCurve()->GetValue(duration * 0.25f); |
| EXPECT_NEAR(3.0333f, value.x(), 0.0002f); |
| EXPECT_NEAR(37.4168f, value.y(), 0.0002f); |
| } |
| |
| TEST(ScrollOffsetAnimationCurveTest, UpdateTarget) { |
| gfx::ScrollOffset initial_value(0.f, 0.f); |
| gfx::ScrollOffset target_value(0.f, 3600.f); |
| double duration = kConstantDuration / kDurationDivisor; |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| target_value, CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT), |
| ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT)); |
| curve->SetInitialValue(initial_value); |
| 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(base::TimeDelta::FromSecondsD(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(base::TimeDelta::FromSecondsD(duration), |
| gfx::ScrollOffset(0.0, 7200.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); |
| } |
| |
| TEST(ScrollOffsetAnimationCurveTest, InverseDeltaDuration) { |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| gfx::ScrollOffset(0.f, 100.f), |
| CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT), |
| ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA)); |
| |
| curve->SetInitialValue(gfx::ScrollOffset()); |
| double smallDeltaDuration = curve->Duration().InSecondsF(); |
| |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.01f), |
| gfx::ScrollOffset(0.f, 300.f)); |
| double mediumDeltaDuration = curve->Duration().InSecondsF(); |
| |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.01f), |
| gfx::ScrollOffset(0.f, 500.f)); |
| double largeDeltaDuration = curve->Duration().InSecondsF(); |
| |
| EXPECT_GT(smallDeltaDuration, mediumDeltaDuration); |
| EXPECT_GT(mediumDeltaDuration, largeDeltaDuration); |
| |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.01f), |
| gfx::ScrollOffset(0.f, 5000.f)); |
| EXPECT_EQ(largeDeltaDuration, curve->Duration().InSecondsF()); |
| } |
| |
| TEST(ScrollOffsetAnimationCurveTest, CurveWithDelay) { |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| gfx::ScrollOffset(0.f, 100.f), |
| CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT), |
| ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA)); |
| double duration_in_seconds = kInverseDeltaMaxDuration / kDurationDivisor; |
| double delay_in_seconds = 0.02; |
| double curve_duration = duration_in_seconds - delay_in_seconds; |
| |
| curve->SetInitialValue(gfx::ScrollOffset(), |
| base::TimeDelta::FromSecondsD(delay_in_seconds)); |
| EXPECT_NEAR(curve_duration, curve->Duration().InSecondsF(), 0.0002f); |
| |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.01f), |
| gfx::ScrollOffset(0.f, 500.f)); |
| EXPECT_GT(curve_duration, curve->Duration().InSecondsF()); |
| EXPECT_EQ(gfx::ScrollOffset(0.f, 500.f), curve->target_value()); |
| } |
| |
| TEST(ScrollOffsetAnimationCurveTest, CurveWithLargeDelay) { |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| gfx::ScrollOffset(0.f, 100.f), |
| CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT), |
| ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA)); |
| curve->SetInitialValue(gfx::ScrollOffset(), |
| base::TimeDelta::FromSecondsD(0.2)); |
| EXPECT_EQ(0.f, curve->Duration().InSecondsF()); |
| |
| // Re-targeting when animation duration is 0. |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.01), |
| gfx::ScrollOffset(0.f, 300.f)); |
| double duration = |
| ScrollOffsetAnimationCurve::SegmentDuration( |
| gfx::Vector2dF(0.f, 200.f), |
| ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA, |
| base::TimeDelta::FromSecondsD(0.01)) |
| .InSecondsF(); |
| EXPECT_EQ(duration, curve->Duration().InSecondsF()); |
| |
| // Re-targeting before last_retarget_, the difference should be accounted for |
| // in duration. |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.01), |
| gfx::ScrollOffset(0.f, 500.f)); |
| duration = ScrollOffsetAnimationCurve::SegmentDuration( |
| gfx::Vector2dF(0.f, 500.f), |
| ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA, |
| base::TimeDelta::FromSecondsD(0.01)) |
| .InSecondsF(); |
| EXPECT_EQ(duration, curve->Duration().InSecondsF()); |
| |
| EXPECT_VECTOR2DF_EQ(gfx::ScrollOffset(0.f, 500.f), |
| curve->GetValue(base::TimeDelta::FromSecondsD(1.0))); |
| } |
| |
| // This test verifies that if the last segment duration is zero, ::UpdateTarget |
| // simply updates the total animation duration see crbug.com/645317. |
| TEST(ScrollOffsetAnimationCurveTest, UpdateTargetZeroLastSegmentDuration) { |
| std::unique_ptr<ScrollOffsetAnimationCurve> curve( |
| ScrollOffsetAnimationCurve::Create( |
| gfx::ScrollOffset(0.f, 100.f), |
| CubicBezierTimingFunction::CreatePreset( |
| CubicBezierTimingFunction::EaseType::EASE_IN_OUT), |
| ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA)); |
| double duration_in_seconds = kInverseDeltaMaxDuration / kDurationDivisor; |
| double delay_in_seconds = 0.02; |
| double curve_duration = duration_in_seconds - delay_in_seconds; |
| |
| curve->SetInitialValue(gfx::ScrollOffset(), |
| base::TimeDelta::FromSecondsD(delay_in_seconds)); |
| EXPECT_NEAR(curve_duration, curve->Duration().InSecondsF(), 0.0002f); |
| |
| // Re-target 1, this should set last_retarget_ to 0.05. |
| gfx::ScrollOffset new_delta = |
| gfx::ScrollOffset(0.f, 200.f) - |
| curve->GetValue(base::TimeDelta::FromSecondsD(0.05)); |
| double expected_duration = |
| ScrollOffsetAnimationCurve::SegmentDuration( |
| gfx::Vector2dF(new_delta.x(), new_delta.y()), |
| ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA, |
| base::TimeDelta()) |
| .InSecondsF() + |
| 0.05; |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.05), |
| gfx::ScrollOffset(0.f, 200.f)); |
| EXPECT_NEAR(expected_duration, curve->Duration().InSecondsF(), 0.0002f); |
| |
| // Re-target 2, this should set total_animation_duration to t, which is |
| // last_retarget_. This is what would cause the DCHECK failure in |
| // crbug.com/645317. |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.145), |
| gfx::ScrollOffset(0.f, 300.f)); |
| EXPECT_NEAR(0.05, curve->Duration().InSecondsF(), 0.0002f); |
| |
| // Re-target 3, this should set total_animation_duration based on new_delta. |
| new_delta = gfx::ScrollOffset(0.f, 500.f) - |
| curve->GetValue(base::TimeDelta::FromSecondsD(0.05)); |
| expected_duration = |
| ScrollOffsetAnimationCurve::SegmentDuration( |
| gfx::Vector2dF(new_delta.x(), new_delta.y()), |
| ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA, |
| base::TimeDelta::FromSecondsD(0.15)) |
| .InSecondsF(); |
| curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.1), |
| gfx::ScrollOffset(0.f, 500.f)); |
| EXPECT_NEAR(expected_duration, curve->Duration().InSecondsF(), 0.0002f); |
| } |
| |
| } // namespace |
| } // namespace cc |