summaryrefslogtreecommitdiff
path: root/chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc')
-rw-r--r--chromium/ui/gfx/animation/keyframe/keyframed_animation_curve.cc129
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