| 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 22 matching lines...) Expand all Loading... |
| 33 namespace cc { | 33 namespace cc { |
| 34 | 34 |
| 35 namespace { | 35 namespace { |
| 36 | 36 |
| 37 const double kEpsilon = 0.01f; | 37 const double kEpsilon = 0.01f; |
| 38 | 38 |
| 39 static float MaximumDimension(const gfx::Vector2dF& delta) { | 39 static float MaximumDimension(const gfx::Vector2dF& delta) { |
| 40 return std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y(); | 40 return std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y(); |
| 41 } | 41 } |
| 42 | 42 |
| 43 static base::TimeDelta SegmentDuration(const gfx::Vector2dF& delta, | |
| 44 DurationBehavior behavior) { | |
| 45 double duration = kConstantDuration; | |
| 46 switch (behavior) { | |
| 47 case DurationBehavior::CONSTANT: | |
| 48 duration = kConstantDuration; | |
| 49 break; | |
| 50 case DurationBehavior::DELTA_BASED: | |
| 51 duration = std::sqrt(std::abs(MaximumDimension(delta))); | |
| 52 break; | |
| 53 case DurationBehavior::INVERSE_DELTA: | |
| 54 duration = std::min( | |
| 55 std::max(kInverseDeltaOffset + | |
| 56 std::abs(MaximumDimension(delta)) * kInverseDeltaSlope, | |
| 57 kInverseDeltaMinDuration), | |
| 58 kInverseDeltaMaxDuration); | |
| 59 break; | |
| 60 default: | |
| 61 NOTREACHED(); | |
| 62 } | |
| 63 return base::TimeDelta::FromMicroseconds(duration / kDurationDivisor * | |
| 64 base::Time::kMicrosecondsPerSecond); | |
| 65 } | |
| 66 | |
| 67 static std::unique_ptr<TimingFunction> EaseOutWithInitialVelocity( | 43 static std::unique_ptr<TimingFunction> EaseOutWithInitialVelocity( |
| 68 double velocity) { | 44 double velocity) { |
| 69 // Clamp velocity to a sane value. | 45 // Clamp velocity to a sane value. |
| 70 velocity = std::min(std::max(velocity, -1000.0), 1000.0); | 46 velocity = std::min(std::max(velocity, -1000.0), 1000.0); |
| 71 | 47 |
| 72 // Based on CubicBezierTimingFunction::EaseType::EASE_IN_OUT preset | 48 // Based on CubicBezierTimingFunction::EaseType::EASE_IN_OUT preset |
| 73 // with first control point scaled. | 49 // with first control point scaled. |
| 74 const double x1 = 0.42; | 50 const double x1 = 0.42; |
| 75 const double y1 = velocity * x1; | 51 const double y1 = velocity * x1; |
| 76 return CubicBezierTimingFunction::Create(x1, y1, 0.58, 1); | 52 return CubicBezierTimingFunction::Create(x1, y1, 0.58, 1); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 90 const gfx::ScrollOffset& target_value, | 66 const gfx::ScrollOffset& target_value, |
| 91 std::unique_ptr<TimingFunction> timing_function, | 67 std::unique_ptr<TimingFunction> timing_function, |
| 92 DurationBehavior duration_behavior) | 68 DurationBehavior duration_behavior) |
| 93 : target_value_(target_value), | 69 : target_value_(target_value), |
| 94 timing_function_(std::move(timing_function)), | 70 timing_function_(std::move(timing_function)), |
| 95 duration_behavior_(duration_behavior), | 71 duration_behavior_(duration_behavior), |
| 96 has_set_initial_value_(false) {} | 72 has_set_initial_value_(false) {} |
| 97 | 73 |
| 98 ScrollOffsetAnimationCurve::~ScrollOffsetAnimationCurve() {} | 74 ScrollOffsetAnimationCurve::~ScrollOffsetAnimationCurve() {} |
| 99 | 75 |
| 76 base::TimeDelta ScrollOffsetAnimationCurve::SegmentDuration( |
| 77 const gfx::Vector2dF& delta, |
| 78 DurationBehavior behavior, |
| 79 base::TimeDelta delayed_by) { |
| 80 double duration = kConstantDuration; |
| 81 switch (behavior) { |
| 82 case DurationBehavior::CONSTANT: |
| 83 duration = kConstantDuration; |
| 84 break; |
| 85 case DurationBehavior::DELTA_BASED: |
| 86 duration = std::sqrt(std::abs(MaximumDimension(delta))); |
| 87 break; |
| 88 case DurationBehavior::INVERSE_DELTA: |
| 89 duration = std::min( |
| 90 std::max(kInverseDeltaOffset + |
| 91 std::abs(MaximumDimension(delta)) * kInverseDeltaSlope, |
| 92 kInverseDeltaMinDuration), |
| 93 kInverseDeltaMaxDuration); |
| 94 break; |
| 95 default: |
| 96 NOTREACHED(); |
| 97 } |
| 98 |
| 99 base::TimeDelta time_delta = base::TimeDelta::FromMicroseconds( |
| 100 duration / kDurationDivisor * base::Time::kMicrosecondsPerSecond); |
| 101 |
| 102 time_delta -= delayed_by; |
| 103 if (time_delta >= base::TimeDelta()) |
| 104 return time_delta; |
| 105 return base::TimeDelta(); |
| 106 } |
| 107 |
| 100 void ScrollOffsetAnimationCurve::SetInitialValue( | 108 void ScrollOffsetAnimationCurve::SetInitialValue( |
| 101 const gfx::ScrollOffset& initial_value) { | 109 const gfx::ScrollOffset& initial_value, |
| 110 base::TimeDelta delayed_by) { |
| 102 initial_value_ = initial_value; | 111 initial_value_ = initial_value; |
| 103 has_set_initial_value_ = true; | 112 has_set_initial_value_ = true; |
| 104 total_animation_duration_ = SegmentDuration( | 113 total_animation_duration_ = SegmentDuration( |
| 105 target_value_.DeltaFrom(initial_value_), duration_behavior_); | 114 target_value_.DeltaFrom(initial_value_), duration_behavior_, delayed_by); |
| 106 } | 115 } |
| 107 | 116 |
| 108 bool ScrollOffsetAnimationCurve::HasSetInitialValue() const { | 117 bool ScrollOffsetAnimationCurve::HasSetInitialValue() const { |
| 109 return has_set_initial_value_; | 118 return has_set_initial_value_; |
| 110 } | 119 } |
| 111 | 120 |
| 112 void ScrollOffsetAnimationCurve::ApplyAdjustment( | 121 void ScrollOffsetAnimationCurve::ApplyAdjustment( |
| 113 const gfx::Vector2dF& adjustment) { | 122 const gfx::Vector2dF& adjustment) { |
| 114 initial_value_ = ScrollOffsetWithDelta(initial_value_, adjustment); | 123 initial_value_ = ScrollOffsetWithDelta(initial_value_, adjustment); |
| 115 target_value_ = ScrollOffsetWithDelta(target_value_, adjustment); | 124 target_value_ = ScrollOffsetWithDelta(target_value_, adjustment); |
| 116 } | 125 } |
| 117 | 126 |
| 118 gfx::ScrollOffset ScrollOffsetAnimationCurve::GetValue( | 127 gfx::ScrollOffset ScrollOffsetAnimationCurve::GetValue( |
| 119 base::TimeDelta t) const { | 128 base::TimeDelta t) const { |
| 120 base::TimeDelta duration = total_animation_duration_ - last_retarget_; | 129 base::TimeDelta duration = total_animation_duration_ - last_retarget_; |
| 121 t -= last_retarget_; | 130 t -= last_retarget_; |
| 122 | 131 |
| 132 if (duration.is_zero()) |
| 133 return target_value_; |
| 134 |
| 123 if (t <= base::TimeDelta()) | 135 if (t <= base::TimeDelta()) |
| 124 return initial_value_; | 136 return initial_value_; |
| 125 | 137 |
| 126 if (t >= duration) | 138 if (t >= duration) |
| 127 return target_value_; | 139 return target_value_; |
| 128 | 140 |
| 129 double progress = timing_function_->GetValue(TimeUtil::Divide(t, duration)); | 141 double progress = timing_function_->GetValue(TimeUtil::Divide(t, duration)); |
| 130 return gfx::ScrollOffset( | 142 return gfx::ScrollOffset( |
| 131 gfx::Tween::FloatValueBetween( | 143 gfx::Tween::FloatValueBetween( |
| 132 progress, initial_value_.x(), target_value_.x()), | 144 progress, initial_value_.x(), target_value_.x()), |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 187 } | 199 } |
| 188 | 200 |
| 189 void ScrollOffsetAnimationCurve::UpdateTarget( | 201 void ScrollOffsetAnimationCurve::UpdateTarget( |
| 190 double t, | 202 double t, |
| 191 const gfx::ScrollOffset& new_target) { | 203 const gfx::ScrollOffset& new_target) { |
| 192 if (std::abs(MaximumDimension(target_value_.DeltaFrom(new_target))) < | 204 if (std::abs(MaximumDimension(target_value_.DeltaFrom(new_target))) < |
| 193 kEpsilon) { | 205 kEpsilon) { |
| 194 target_value_ = new_target; | 206 target_value_ = new_target; |
| 195 return; | 207 return; |
| 196 } | 208 } |
| 209 |
| 210 // The total_animation_duration_ is zero because of the delay that we |
| 211 // accounted for when the animation was created. The new duration should |
| 212 // also take the delay into account. |
| 213 if (total_animation_duration_.is_zero()) { |
| 214 DCHECK_LE(t, 0); |
| 215 total_animation_duration_ = |
| 216 SegmentDuration(new_target.DeltaFrom(initial_value_), |
| 217 duration_behavior_, base::TimeDelta::FromSecondsD(-t)); |
| 218 target_value_ = new_target; |
| 219 return; |
| 220 } |
| 221 |
| 222 base::TimeDelta delayed_by = base::TimeDelta::FromSecondsD( |
| 223 std::max(0.0, last_retarget_.InSecondsF() - t)); |
| 224 t = std::max(t, last_retarget_.InSecondsF()); |
| 225 |
| 197 gfx::ScrollOffset current_position = | 226 gfx::ScrollOffset current_position = |
| 198 GetValue(base::TimeDelta::FromSecondsD(t)); | 227 GetValue(base::TimeDelta::FromSecondsD(t)); |
| 199 gfx::Vector2dF old_delta = target_value_.DeltaFrom(initial_value_); | 228 gfx::Vector2dF old_delta = target_value_.DeltaFrom(initial_value_); |
| 200 gfx::Vector2dF new_delta = new_target.DeltaFrom(current_position); | 229 gfx::Vector2dF new_delta = new_target.DeltaFrom(current_position); |
| 201 | 230 |
| 202 double old_duration = | 231 double old_duration = |
| 203 (total_animation_duration_ - last_retarget_).InSecondsF(); | 232 (total_animation_duration_ - last_retarget_).InSecondsF(); |
| 204 double old_normalized_velocity = timing_function_->Velocity( | 233 double old_normalized_velocity = timing_function_->Velocity( |
| 205 (t - last_retarget_.InSecondsF()) / old_duration); | 234 (t - last_retarget_.InSecondsF()) / old_duration); |
| 206 | 235 |
| 207 // Use the velocity-based duration bound when it is less than the constant | 236 // Use the velocity-based duration bound when it is less than the constant |
| 208 // segment duration. This minimizes the "rubber-band" bouncing effect when | 237 // segment duration. This minimizes the "rubber-band" bouncing effect when |
| 209 // old_normalized_velocity is large and new_delta is small. | 238 // old_normalized_velocity is large and new_delta is small. |
| 210 double new_duration = | 239 double new_duration = std::min( |
| 211 std::min(SegmentDuration(new_delta, duration_behavior_).InSecondsF(), | 240 SegmentDuration(new_delta, duration_behavior_, delayed_by).InSecondsF(), |
| 212 VelocityBasedDurationBound(old_delta, old_normalized_velocity, | 241 VelocityBasedDurationBound(old_delta, old_normalized_velocity, |
| 213 old_duration, new_delta)); | 242 old_duration, new_delta)); |
| 214 | 243 |
| 215 if (new_duration < kEpsilon) { | 244 if (new_duration < kEpsilon) { |
| 216 // We are already at or very close to the new target. Stop animating. | 245 // We are already at or very close to the new target. Stop animating. |
| 217 target_value_ = new_target; | 246 target_value_ = new_target; |
| 218 total_animation_duration_ = base::TimeDelta::FromSecondsD(t); | 247 total_animation_duration_ = base::TimeDelta::FromSecondsD(t); |
| 219 return; | 248 return; |
| 220 } | 249 } |
| 221 | 250 |
| 222 // TimingFunction::Velocity gives the slope of the curve from 0 to 1. | 251 // 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 | 252 // 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. | 253 // differences in duration and scroll delta between old and new curves. |
| 225 double new_normalized_velocity = | 254 double new_normalized_velocity = |
| 226 old_normalized_velocity * (new_duration / old_duration) * | 255 old_normalized_velocity * (new_duration / old_duration) * |
| 227 (MaximumDimension(old_delta) / MaximumDimension(new_delta)); | 256 (MaximumDimension(old_delta) / MaximumDimension(new_delta)); |
| 228 | 257 |
| 229 initial_value_ = current_position; | 258 initial_value_ = current_position; |
| 230 target_value_ = new_target; | 259 target_value_ = new_target; |
| 231 total_animation_duration_ = base::TimeDelta::FromSecondsD(t + new_duration); | 260 total_animation_duration_ = base::TimeDelta::FromSecondsD(t + new_duration); |
| 232 last_retarget_ = base::TimeDelta::FromSecondsD(t); | 261 last_retarget_ = base::TimeDelta::FromSecondsD(t); |
| 233 timing_function_ = EaseOutWithInitialVelocity(new_normalized_velocity); | 262 timing_function_ = EaseOutWithInitialVelocity(new_normalized_velocity); |
| 234 } | 263 } |
| 235 | 264 |
| 236 } // namespace cc | 265 } // namespace cc |
| OLD | NEW |