diff options
Diffstat (limited to 'chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc')
-rw-r--r-- | chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc b/chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc index 90671be1822..3aba6cf916d 100644 --- a/chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc +++ b/chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc @@ -11,11 +11,78 @@ #include <utility> #include "base/memory/ptr_util.h" +#include "base/numerics/ranges.h" +#include "base/time/time.h" #include "ui/gfx/animation/keyframe/keyframed_animation_curve-inl.h" #include "ui/gfx/animation/tween.h" #include "ui/gfx/geometry/box_f.h" namespace gfx { +namespace { + +static constexpr float kTolerance = 1e-5f; + +template <typename KeyframeType, typename ValueType, typename TargetType> +std::unique_ptr<AnimationCurve> RetargettedCurve( + std::vector<std::unique_ptr<KeyframeType>>& keyframes, + base::TimeDelta t, + const ValueType& value_at_t, + const ValueType& new_target_value, + double scaled_duration, + TargetType* target, + const TimingFunction* timing_function) { + if (SufficientlyEqual(keyframes.back()->Value(), new_target_value)) + return nullptr; + + DCHECK_GE(keyframes.size(), 2u); + DCHECK_GT(scaled_duration, 0.f); + + // If we haven't progressed to animating between the last 2 keyframes, simply + // clobber the value for the last keyframe. + const bool at_last_keyframe = + (keyframes[keyframes.size() - 2]->Time() * scaled_duration) <= t; + if (!at_last_keyframe) { + auto& last_keyframe = keyframes.back(); + auto* keyframe_timing_function = last_keyframe->timing_function(); + last_keyframe = KeyframeType::Create( + last_keyframe->Time(), new_target_value, + keyframe_timing_function ? keyframe_timing_function->Clone() : nullptr); + return nullptr; + } + + // Ensure that `t` happens between the last two keyframes. + DCHECK_GE(keyframes[keyframes.size() - 1]->Time() * scaled_duration, t); + + // TODO(crbug.com/1198305): This can be changed to a different / special + // interpolation curve type to maintain c2 continuity. + auto curve = AnimationTraits<ValueType>::KeyframedCurveType::Create(); + curve->set_scaled_duration(scaled_duration); + curve->set_target(target); + + auto generate_timing_function = + [timing_function]() -> std::unique_ptr<gfx::TimingFunction> { + if (timing_function) + return timing_function->Clone(); + return nullptr; + }; + + // Keep the curve duration the same by adding the same first frame. + curve->AddKeyframe(KeyframeType::Create(keyframes.front()->Time(), + keyframes.front()->Value(), + generate_timing_function())); + + // Snap the current value at `t` so that the current value stays the same. + curve->AddKeyframe(KeyframeType::Create(t / scaled_duration, value_at_t, + generate_timing_function())); + + // Add a new target at the same time as the last frame. + curve->AddKeyframe(KeyframeType::Create( + keyframes.back()->Time(), new_target_value, generate_timing_function())); + + return curve; +} + +} // namespace Keyframe::Keyframe(base::TimeDelta time, std::unique_ptr<TimingFunction> timing_function) @@ -213,6 +280,14 @@ SkColor KeyframedColorAnimationCurve::GetValue(base::TimeDelta t) const { keyframes_[i + 1]->Value()); } +std::unique_ptr<AnimationCurve> KeyframedColorAnimationCurve::Retarget( + base::TimeDelta t, + SkColor new_target) { + DCHECK(!keyframes_.empty()); + return RetargettedCurve(keyframes_, t, GetValue(t), new_target, + scaled_duration(), target(), timing_function_.get()); +} + std::unique_ptr<KeyframedFloatAnimationCurve> KeyframedFloatAnimationCurve::Create() { return base::WrapUnique(new KeyframedFloatAnimationCurve); @@ -251,6 +326,14 @@ std::unique_ptr<AnimationCurve> KeyframedFloatAnimationCurve::Clone() const { return std::move(to_return); } +std::unique_ptr<AnimationCurve> KeyframedFloatAnimationCurve::Retarget( + base::TimeDelta t, + float new_target) { + DCHECK(!keyframes_.empty()); + return RetargettedCurve(keyframes_, t, GetValue(t), new_target, + scaled_duration(), target(), timing_function_.get()); +} + float KeyframedFloatAnimationCurve::GetValue(base::TimeDelta t) const { if (t <= (keyframes_.front()->Time() * scaled_duration())) return keyframes_.front()->Value(); @@ -344,6 +427,14 @@ bool KeyframedTransformAnimationCurve::MaximumScale(float* max_scale) const { return *max_scale > 0.f; } +std::unique_ptr<AnimationCurve> KeyframedTransformAnimationCurve::Retarget( + base::TimeDelta t, + const gfx::TransformOperations& new_target) { + DCHECK(!keyframes_.empty()); + return RetargettedCurve(keyframes_, t, GetValue(t), new_target, + scaled_duration(), target(), timing_function_.get()); +} + std::unique_ptr<KeyframedSizeAnimationCurve> KeyframedSizeAnimationCurve::Create() { return base::WrapUnique(new KeyframedSizeAnimationCurve); @@ -399,6 +490,14 @@ gfx::SizeF KeyframedSizeAnimationCurve::GetValue(base::TimeDelta t) const { keyframes_[i + 1]->Value()); } +std::unique_ptr<AnimationCurve> KeyframedSizeAnimationCurve::Retarget( + base::TimeDelta t, + const gfx::SizeF& new_target) { + DCHECK(!keyframes_.empty()); + return RetargettedCurve(keyframes_, t, GetValue(t), new_target, + scaled_duration(), target(), timing_function_.get()); +} + std::unique_ptr<KeyframedRectAnimationCurve> KeyframedRectAnimationCurve::Create() { return base::WrapUnique(new KeyframedRectAnimationCurve); @@ -454,4 +553,34 @@ gfx::Rect KeyframedRectAnimationCurve::GetValue(base::TimeDelta t) const { keyframes_[i + 1]->Value()); } +std::unique_ptr<AnimationCurve> KeyframedRectAnimationCurve::Retarget( + base::TimeDelta t, + const gfx::Rect& new_target) { + DCHECK(!keyframes_.empty()); + return RetargettedCurve(keyframes_, t, GetValue(t), new_target, + scaled_duration(), target(), timing_function_.get()); +} + +bool SufficientlyEqual(float lhs, float rhs) { + return base::IsApproximatelyEqual(lhs, rhs, kTolerance); +} + +bool SufficientlyEqual(const TransformOperations& lhs, + const TransformOperations& rhs) { + return lhs.ApproximatelyEqual(rhs, kTolerance); +} + +bool SufficientlyEqual(const SizeF& lhs, const SizeF& rhs) { + return base::IsApproximatelyEqual(lhs.width(), rhs.width(), kTolerance) && + base::IsApproximatelyEqual(lhs.height(), rhs.height(), kTolerance); +} + +bool SufficientlyEqual(SkColor lhs, SkColor rhs) { + return lhs == rhs; +} + +bool SufficientlyEqual(const Rect& lhs, const Rect& rhs) { + return lhs == rhs; +} + } // namespace gfx |