Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "cc/animation/scroll_offset_animation_curve.h" | 5 #include "cc/animation/scroll_offset_animation_curve.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 const double kInverseDeltaMinDuration = 6.0; | 23 const double kInverseDeltaMinDuration = 6.0; |
| 24 const double kInverseDeltaMaxDuration = 12.0; | 24 const double kInverseDeltaMaxDuration = 12.0; |
| 25 | 25 |
| 26 const double kInverseDeltaSlope = | 26 const double kInverseDeltaSlope = |
| 27 (kInverseDeltaMinDuration - kInverseDeltaMaxDuration) / | 27 (kInverseDeltaMinDuration - kInverseDeltaMaxDuration) / |
| 28 (kInverseDeltaRampEndPx - kInverseDeltaRampStartPx); | 28 (kInverseDeltaRampEndPx - kInverseDeltaRampStartPx); |
| 29 | 29 |
| 30 const double kInverseDeltaOffset = | 30 const double kInverseDeltaOffset = |
| 31 kInverseDeltaMaxDuration - kInverseDeltaRampStartPx * kInverseDeltaSlope; | 31 kInverseDeltaMaxDuration - kInverseDeltaRampStartPx * kInverseDeltaSlope; |
| 32 | 32 |
| 33 const double kMinDuration = 5.0; | |
| 34 | |
| 33 namespace cc { | 35 namespace cc { |
| 34 | 36 |
| 35 namespace { | 37 namespace { |
| 36 | 38 |
| 37 const double kEpsilon = 0.01f; | 39 const double kEpsilon = 0.01f; |
| 38 | 40 |
| 39 static float MaximumDimension(const gfx::Vector2dF& delta) { | 41 static float MaximumDimension(const gfx::Vector2dF& delta) { |
| 40 return std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y(); | 42 return std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y(); |
| 41 } | 43 } |
| 42 | 44 |
| 43 static base::TimeDelta SegmentDuration(const gfx::Vector2dF& delta, | 45 static base::TimeDelta SegmentDuration(const gfx::Vector2dF& delta, |
| 44 DurationBehavior behavior) { | 46 DurationBehavior behavior, |
| 47 base::TimeDelta jank_adjustment) { | |
| 45 double duration = kConstantDuration; | 48 double duration = kConstantDuration; |
| 46 switch (behavior) { | 49 switch (behavior) { |
| 47 case DurationBehavior::CONSTANT: | 50 case DurationBehavior::CONSTANT: |
| 48 duration = kConstantDuration; | 51 duration = kConstantDuration; |
| 49 break; | 52 break; |
| 50 case DurationBehavior::DELTA_BASED: | 53 case DurationBehavior::DELTA_BASED: |
| 51 duration = std::sqrt(std::abs(MaximumDimension(delta))); | 54 duration = std::sqrt(std::abs(MaximumDimension(delta))); |
| 52 break; | 55 break; |
| 53 case DurationBehavior::INVERSE_DELTA: | 56 case DurationBehavior::INVERSE_DELTA: |
| 54 duration = std::min( | 57 duration = std::min( |
| 55 std::max(kInverseDeltaOffset + | 58 std::max(kInverseDeltaOffset + |
| 56 std::abs(MaximumDimension(delta)) * kInverseDeltaSlope, | 59 std::abs(MaximumDimension(delta)) * kInverseDeltaSlope, |
| 57 kInverseDeltaMinDuration), | 60 kInverseDeltaMinDuration), |
| 58 kInverseDeltaMaxDuration); | 61 kInverseDeltaMaxDuration); |
| 59 break; | 62 break; |
| 60 default: | 63 default: |
| 61 NOTREACHED(); | 64 NOTREACHED(); |
| 62 } | 65 } |
| 63 return base::TimeDelta::FromMicroseconds(duration / kDurationDivisor * | 66 |
| 64 base::Time::kMicrosecondsPerSecond); | 67 base::TimeDelta td = base::TimeDelta::FromMicroseconds( |
| 68 duration / kDurationDivisor * base::Time::kMicrosecondsPerSecond); | |
| 69 base::TimeDelta min_td = base::TimeDelta::FromMicroseconds( | |
| 70 kMinDuration / kDurationDivisor * base::Time::kMicrosecondsPerSecond); | |
| 71 // jank_adjustment is the time delay between now and when we were actually | |
| 72 // asked to scroll. If the delay is large, setting the duration to 0 will | |
| 73 // scroll to the target position instantly. | |
| 74 if (td - jank_adjustment > min_td) | |
| 75 return td - jank_adjustment; | |
| 76 return base::TimeDelta(); | |
|
ymalik
2016/06/21 23:46:02
@skobes, If the animation duration is < 83ms, this
| |
| 65 } | 77 } |
| 66 | 78 |
| 67 static std::unique_ptr<TimingFunction> EaseOutWithInitialVelocity( | 79 static std::unique_ptr<TimingFunction> EaseOutWithInitialVelocity( |
| 68 double velocity) { | 80 double velocity) { |
| 69 // Clamp velocity to a sane value. | 81 // Clamp velocity to a sane value. |
| 70 velocity = std::min(std::max(velocity, -1000.0), 1000.0); | 82 velocity = std::min(std::max(velocity, -1000.0), 1000.0); |
| 71 | 83 |
| 72 // Based on CubicBezierTimingFunction::EaseType::EASE_IN_OUT preset | 84 // Based on CubicBezierTimingFunction::EaseType::EASE_IN_OUT preset |
| 73 // with first control point scaled. | 85 // with first control point scaled. |
| 74 const double x1 = 0.42; | 86 const double x1 = 0.42; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 91 std::unique_ptr<TimingFunction> timing_function, | 103 std::unique_ptr<TimingFunction> timing_function, |
| 92 DurationBehavior duration_behavior) | 104 DurationBehavior duration_behavior) |
| 93 : target_value_(target_value), | 105 : target_value_(target_value), |
| 94 timing_function_(std::move(timing_function)), | 106 timing_function_(std::move(timing_function)), |
| 95 duration_behavior_(duration_behavior), | 107 duration_behavior_(duration_behavior), |
| 96 has_set_initial_value_(false) {} | 108 has_set_initial_value_(false) {} |
| 97 | 109 |
| 98 ScrollOffsetAnimationCurve::~ScrollOffsetAnimationCurve() {} | 110 ScrollOffsetAnimationCurve::~ScrollOffsetAnimationCurve() {} |
| 99 | 111 |
| 100 void ScrollOffsetAnimationCurve::SetInitialValue( | 112 void ScrollOffsetAnimationCurve::SetInitialValue( |
| 101 const gfx::ScrollOffset& initial_value) { | 113 const gfx::ScrollOffset& initial_value, |
| 114 base::TimeDelta jank_adjustment) { | |
| 102 initial_value_ = initial_value; | 115 initial_value_ = initial_value; |
| 103 has_set_initial_value_ = true; | 116 has_set_initial_value_ = true; |
| 104 total_animation_duration_ = SegmentDuration( | 117 total_animation_duration_ = |
| 105 target_value_.DeltaFrom(initial_value_), duration_behavior_); | 118 SegmentDuration(target_value_.DeltaFrom(initial_value_), |
| 119 duration_behavior_, jank_adjustment); | |
| 106 } | 120 } |
| 107 | 121 |
| 108 bool ScrollOffsetAnimationCurve::HasSetInitialValue() const { | 122 bool ScrollOffsetAnimationCurve::HasSetInitialValue() const { |
| 109 return has_set_initial_value_; | 123 return has_set_initial_value_; |
| 110 } | 124 } |
| 111 | 125 |
| 112 void ScrollOffsetAnimationCurve::ApplyAdjustment( | 126 void ScrollOffsetAnimationCurve::ApplyAdjustment( |
| 113 const gfx::Vector2dF& adjustment) { | 127 const gfx::Vector2dF& adjustment) { |
| 114 initial_value_ = ScrollOffsetWithDelta(initial_value_, adjustment); | 128 initial_value_ = ScrollOffsetWithDelta(initial_value_, adjustment); |
| 115 target_value_ = ScrollOffsetWithDelta(target_value_, adjustment); | 129 target_value_ = ScrollOffsetWithDelta(target_value_, adjustment); |
| 116 } | 130 } |
| 117 | 131 |
| 118 gfx::ScrollOffset ScrollOffsetAnimationCurve::GetValue( | 132 gfx::ScrollOffset ScrollOffsetAnimationCurve::GetValue( |
| 119 base::TimeDelta t) const { | 133 base::TimeDelta t) const { |
| 120 base::TimeDelta duration = total_animation_duration_ - last_retarget_; | 134 base::TimeDelta duration = total_animation_duration_ - last_retarget_; |
| 121 t -= last_retarget_; | 135 t -= last_retarget_; |
| 122 | 136 |
| 137 if (t >= duration) | |
| 138 return target_value_; | |
| 139 | |
| 123 if (t <= base::TimeDelta()) | 140 if (t <= base::TimeDelta()) |
| 124 return initial_value_; | 141 return initial_value_; |
| 125 | 142 |
| 126 if (t >= duration) | |
| 127 return target_value_; | |
| 128 | |
| 129 double progress = timing_function_->GetValue(TimeUtil::Divide(t, duration)); | 143 double progress = timing_function_->GetValue(TimeUtil::Divide(t, duration)); |
| 130 return gfx::ScrollOffset( | 144 return gfx::ScrollOffset( |
| 131 gfx::Tween::FloatValueBetween( | 145 gfx::Tween::FloatValueBetween( |
| 132 progress, initial_value_.x(), target_value_.x()), | 146 progress, initial_value_.x(), target_value_.x()), |
| 133 gfx::Tween::FloatValueBetween( | 147 gfx::Tween::FloatValueBetween( |
| 134 progress, initial_value_.y(), target_value_.y())); | 148 progress, initial_value_.y(), target_value_.y())); |
| 135 } | 149 } |
| 136 | 150 |
| 137 base::TimeDelta ScrollOffsetAnimationCurve::Duration() const { | 151 base::TimeDelta ScrollOffsetAnimationCurve::Duration() const { |
| 138 return total_animation_duration_; | 152 return total_animation_duration_; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 194 target_value_ = new_target; | 208 target_value_ = new_target; |
| 195 return; | 209 return; |
| 196 } | 210 } |
| 197 gfx::ScrollOffset current_position = | 211 gfx::ScrollOffset current_position = |
| 198 GetValue(base::TimeDelta::FromSecondsD(t)); | 212 GetValue(base::TimeDelta::FromSecondsD(t)); |
| 199 gfx::Vector2dF old_delta = target_value_.DeltaFrom(initial_value_); | 213 gfx::Vector2dF old_delta = target_value_.DeltaFrom(initial_value_); |
| 200 gfx::Vector2dF new_delta = new_target.DeltaFrom(current_position); | 214 gfx::Vector2dF new_delta = new_target.DeltaFrom(current_position); |
| 201 | 215 |
| 202 double old_duration = | 216 double old_duration = |
| 203 (total_animation_duration_ - last_retarget_).InSecondsF(); | 217 (total_animation_duration_ - last_retarget_).InSecondsF(); |
| 218 | |
| 219 if (old_duration < kEpsilon) { | |
| 220 // We had an almost instant animation previously, don't try to animate. | |
| 221 target_value_ = new_target; | |
| 222 return; | |
| 223 } | |
| 224 | |
|
ymalik
2016/06/21 23:46:02
Reasoning: I think updating an animation which had
| |
| 204 double old_normalized_velocity = timing_function_->Velocity( | 225 double old_normalized_velocity = timing_function_->Velocity( |
| 205 (t - last_retarget_.InSecondsF()) / old_duration); | 226 (t - last_retarget_.InSecondsF()) / old_duration); |
| 206 | 227 |
| 207 // Use the velocity-based duration bound when it is less than the constant | 228 // Use the velocity-based duration bound when it is less than the constant |
| 208 // segment duration. This minimizes the "rubber-band" bouncing effect when | 229 // segment duration. This minimizes the "rubber-band" bouncing effect when |
| 209 // old_normalized_velocity is large and new_delta is small. | 230 // old_normalized_velocity is large and new_delta is small. |
| 210 double new_duration = | 231 double new_duration = |
| 211 std::min(SegmentDuration(new_delta, duration_behavior_).InSecondsF(), | 232 std::min(SegmentDuration(new_delta, duration_behavior_, base::TimeDelta()) |
| 233 .InSecondsF(), | |
| 212 VelocityBasedDurationBound(old_delta, old_normalized_velocity, | 234 VelocityBasedDurationBound(old_delta, old_normalized_velocity, |
| 213 old_duration, new_delta)); | 235 old_duration, new_delta)); |
| 214 | 236 |
| 215 if (new_duration < kEpsilon) { | 237 if (new_duration < kEpsilon) { |
| 216 // We are already at or very close to the new target. Stop animating. | 238 // We are already at or very close to the new target. Stop animating. |
| 217 target_value_ = new_target; | 239 target_value_ = new_target; |
| 218 total_animation_duration_ = base::TimeDelta::FromSecondsD(t); | 240 total_animation_duration_ = base::TimeDelta::FromSecondsD(t); |
| 219 return; | 241 return; |
| 220 } | 242 } |
| 221 | 243 |
| 222 // TimingFunction::Velocity gives the slope of the curve from 0 to 1. | 244 // TimingFunction::Velocity gives the slope of the curve from 0 to 1. |
| 223 // To match the "true" velocity in px/sec we must adjust this slope for | 245 // To match the "true" velocity in px/sec we must adjust this slope for |
| 224 // differences in duration and scroll delta between old and new curves. | 246 // differences in duration and scroll delta between old and new curves. |
| 225 double new_normalized_velocity = | 247 double new_normalized_velocity = |
| 226 old_normalized_velocity * (new_duration / old_duration) * | 248 old_normalized_velocity * (new_duration / old_duration) * |
| 227 (MaximumDimension(old_delta) / MaximumDimension(new_delta)); | 249 (MaximumDimension(old_delta) / MaximumDimension(new_delta)); |
| 228 | 250 |
| 229 initial_value_ = current_position; | 251 initial_value_ = current_position; |
| 230 target_value_ = new_target; | 252 target_value_ = new_target; |
| 231 total_animation_duration_ = base::TimeDelta::FromSecondsD(t + new_duration); | 253 total_animation_duration_ = base::TimeDelta::FromSecondsD(t + new_duration); |
| 232 last_retarget_ = base::TimeDelta::FromSecondsD(t); | 254 last_retarget_ = base::TimeDelta::FromSecondsD(t); |
| 233 timing_function_ = EaseOutWithInitialVelocity(new_normalized_velocity); | 255 timing_function_ = EaseOutWithInitialVelocity(new_normalized_velocity); |
| 234 } | 256 } |
| 235 | 257 |
| 236 } // namespace cc | 258 } // namespace cc |
| OLD | NEW |