blob: 46304b16e212829cf16a1c193fbd1c318014d606 [file] [log] [blame]
// Copyright 2017 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 "ui/gfx/animation/keyframe/keyframe_effect.h"
#include <algorithm>
#include "base/containers/cxx20_erase.h"
#include "ui/gfx/animation/keyframe/animation_curve.h"
#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h"
namespace gfx {
namespace {
static int s_next_keyframe_model_id = 1;
static int s_next_group_id = 1;
void ReverseKeyframeModel(base::TimeTicks monotonic_time,
KeyframeModel* keyframe_model) {
keyframe_model->set_direction(keyframe_model->direction() ==
KeyframeModel::Direction::NORMAL
? KeyframeModel::Direction::REVERSE
: KeyframeModel::Direction::NORMAL);
// Our goal here is to reverse the given keyframe_model. That is, if
// we're 20% of the way through the keyframe_model in the forward direction,
// we'd like to be 80% of the way of the reversed keyframe model (so it will
// end quickly).
//
// We can modify our "progress" through an animation by modifying the "time
// offset", a value added to the current time by the animation system before
// applying any other adjustments.
//
// Let our start time be s, our current time be t, and our final time (or
// duration) be d. After reversing the keyframe_model, we would like to start
// sampling from d - t as depicted below.
//
// Forward:
// s t d
// |----|-------------------------|
//
// Reversed:
// s t d
// |----|--------------------|----|
// -----time-offset----->
//
// Now, if we let o represent our desired offset, we need to ensure that
// t = d - (o + t)
//
// That is, sampling at the current time in either the forward or reverse
// curves must result in the same value, otherwise we'll get jank.
//
// This implies that,
// 0 = d - o - 2t
// o = d - 2t
//
// Now if there was a previous offset, we must adjust d by that offset before
// performing this computation, so it becomes d - o_old - 2t:
keyframe_model->set_time_offset(
keyframe_model->curve()->Duration() - keyframe_model->time_offset() -
(2 * (monotonic_time - keyframe_model->start_time())));
}
std::unique_ptr<CubicBezierTimingFunction> CreateTransitionTimingFunction() {
return CubicBezierTimingFunction::CreatePreset(
CubicBezierTimingFunction::EaseType::EASE);
}
base::TimeDelta GetStartTime(KeyframeModel* keyframe_model) {
if (keyframe_model->direction() == KeyframeModel::Direction::NORMAL) {
return base::TimeDelta();
}
return keyframe_model->curve()->Duration();
}
base::TimeDelta GetEndTime(KeyframeModel* keyframe_model) {
if (keyframe_model->direction() == KeyframeModel::Direction::REVERSE) {
return base::TimeDelta();
}
return keyframe_model->curve()->Duration();
}
template <typename ValueType>
void TransitionValueTo(KeyframeEffect* animator,
typename AnimationTraits<ValueType>::TargetType* target,
base::TimeTicks monotonic_time,
int target_property,
const ValueType& from,
const ValueType& to) {
DCHECK(target);
if (animator->transition().target_properties.find(target_property) ==
animator->transition().target_properties.end()) {
AnimationTraits<ValueType>::OnValueAnimated(target, to, target_property);
return;
}
KeyframeModel* running_keyframe_model =
animator->GetRunningKeyframeModelForProperty(target_property);
ValueType effective_current = from;
if (running_keyframe_model) {
const auto* curve = AnimationTraits<ValueType>::ToDerivedCurve(
running_keyframe_model->curve());
if (running_keyframe_model->IsFinishedAt(monotonic_time)) {
effective_current = curve->GetValue(GetEndTime(running_keyframe_model));
} else {
if (SufficientlyEqual(
to, curve->GetValue(GetEndTime(running_keyframe_model)))) {
return;
}
if (SufficientlyEqual(
to, curve->GetValue(GetStartTime(running_keyframe_model)))) {
ReverseKeyframeModel(monotonic_time, running_keyframe_model);
return;
}
}
} else if (SufficientlyEqual(to, from)) {
return;
}
animator->RemoveKeyframeModels(target_property);
std::unique_ptr<typename AnimationTraits<ValueType>::KeyframedCurveType>
curve(AnimationTraits<ValueType>::KeyframedCurveType::Create());
curve->AddKeyframe(AnimationTraits<ValueType>::KeyframeType::Create(
base::TimeDelta(), effective_current, CreateTransitionTimingFunction()));
curve->AddKeyframe(AnimationTraits<ValueType>::KeyframeType::Create(
animator->transition().duration, to, CreateTransitionTimingFunction()));
curve->set_target(target);
animator->AddKeyframeModel(KeyframeModel::Create(
std::move(curve), KeyframeEffect::GetNextKeyframeModelId(),
target_property));
}
} // namespace
int KeyframeEffect::GetNextKeyframeModelId() {
return s_next_keyframe_model_id++;
}
int KeyframeEffect::GetNextGroupId() {
return s_next_group_id++;
}
KeyframeEffect::KeyframeEffect() = default;
KeyframeEffect::KeyframeEffect(KeyframeEffect&&) = default;
KeyframeEffect::~KeyframeEffect() = default;
void KeyframeEffect::AddKeyframeModel(
std::unique_ptr<KeyframeModel> keyframe_model) {
keyframe_models_.push_back(std::move(keyframe_model));
}
void KeyframeEffect::RemoveKeyframeModel(int keyframe_model_id) {
// Since we want to use the KeyframeModels that we're going to remove, we
// need to use a stable_partition here instead of remove_if. remove_if leaves
// the removed items in an unspecified state.
auto keyframe_models_to_remove = std::stable_partition(
keyframe_models_.begin(), keyframe_models_.end(),
[keyframe_model_id](
const std::unique_ptr<gfx::KeyframeModel>& keyframe_model) {
return keyframe_model->id() != keyframe_model_id;
});
RemoveKeyframeModelRange(keyframe_models_to_remove, keyframe_models_.end());
}
void KeyframeEffect::RemoveKeyframeModels(int target_property) {
auto keyframe_models_to_remove = std::stable_partition(
keyframe_models_.begin(), keyframe_models_.end(),
[target_property](
const std::unique_ptr<gfx::KeyframeModel>& keyframe_model) {
return keyframe_model->TargetProperty() != target_property;
});
RemoveKeyframeModelRange(keyframe_models_to_remove, keyframe_models_.end());
}
void KeyframeEffect::RemoveAllKeyframeModels() {
RemoveKeyframeModelRange(keyframe_models_.begin(), keyframe_models_.end());
}
void KeyframeEffect::Tick(base::TimeTicks monotonic_time) {
TickInternal(monotonic_time, true);
}
void KeyframeEffect::RemoveKeyframeModelRange(
typename KeyframeModels::iterator to_remove_begin,
typename KeyframeModels::iterator to_remove_end) {
keyframe_models_.erase(to_remove_begin, to_remove_end);
}
void KeyframeEffect::TickKeyframeModel(base::TimeTicks monotonic_time,
KeyframeModel* keyframe_model) {
if ((keyframe_model->run_state() != KeyframeModel::STARTING &&
keyframe_model->run_state() != KeyframeModel::RUNNING &&
keyframe_model->run_state() != KeyframeModel::PAUSED) ||
!keyframe_model->HasActiveTime(monotonic_time)) {
return;
}
AnimationCurve* curve = keyframe_model->curve();
base::TimeDelta trimmed =
keyframe_model->TrimTimeToCurrentIteration(monotonic_time);
curve->Tick(trimmed, keyframe_model->TargetProperty(), keyframe_model);
}
void KeyframeEffect::TickInternal(base::TimeTicks monotonic_time,
bool include_infinite_animations) {
StartKeyframeModels(monotonic_time, include_infinite_animations);
for (auto& keyframe_model : keyframe_models_) {
if (!include_infinite_animations &&
keyframe_model->iterations() == std::numeric_limits<double>::infinity())
continue;
TickKeyframeModel(monotonic_time, keyframe_model.get());
}
// Remove finished keyframe_models.
base::EraseIf(
keyframe_models_,
[monotonic_time](const std::unique_ptr<KeyframeModel>& keyframe_model) {
return !keyframe_model->is_finished() &&
keyframe_model->IsFinishedAt(monotonic_time);
});
StartKeyframeModels(monotonic_time, include_infinite_animations);
}
void KeyframeEffect::FinishAll() {
base::TimeTicks now = base::TimeTicks::Now();
const bool include_infinite_animations = false;
TickInternal(now, include_infinite_animations);
TickInternal(base::TimeTicks::Max(), include_infinite_animations);
#ifndef NDEBUG
for (auto& keyframe_model : keyframe_models_) {
DCHECK_EQ(std::numeric_limits<double>::infinity(),
keyframe_model->iterations());
}
#endif
}
void KeyframeEffect::SetTransitionedProperties(
const std::set<int>& properties) {
transition_.target_properties = properties;
}
void KeyframeEffect::SetTransitionDuration(base::TimeDelta delta) {
transition_.duration = delta;
}
void KeyframeEffect::TransitionFloatTo(FloatAnimationCurve::Target* target,
base::TimeTicks monotonic_time,
int target_property,
float from,
float to) {
TransitionValueTo<float>(this, target, monotonic_time, target_property, from,
to);
}
void KeyframeEffect::TransitionTransformOperationsTo(
TransformAnimationCurve::Target* target,
base::TimeTicks monotonic_time,
int target_property,
const gfx::TransformOperations& from,
const gfx::TransformOperations& to) {
TransitionValueTo<gfx::TransformOperations>(this, target, monotonic_time,
target_property, from, to);
}
void KeyframeEffect::TransitionSizeTo(SizeAnimationCurve::Target* target,
base::TimeTicks monotonic_time,
int target_property,
const gfx::SizeF& from,
const gfx::SizeF& to) {
TransitionValueTo<gfx::SizeF>(this, target, monotonic_time, target_property,
from, to);
}
void KeyframeEffect::TransitionColorTo(ColorAnimationCurve::Target* target,
base::TimeTicks monotonic_time,
int target_property,
SkColor from,
SkColor to) {
TransitionValueTo<SkColor>(this, target, monotonic_time, target_property,
from, to);
}
bool KeyframeEffect::IsAnimatingProperty(int property) const {
for (auto& keyframe_model : keyframe_models_) {
if (keyframe_model->TargetProperty() == property)
return true;
}
return false;
}
bool KeyframeEffect::IsAnimating() const {
return !keyframe_models_.empty();
}
float KeyframeEffect::GetTargetFloatValue(int target_property,
float default_value) const {
return GetTargetValue<float>(target_property, default_value);
}
gfx::TransformOperations KeyframeEffect::GetTargetTransformOperationsValue(
int target_property,
const gfx::TransformOperations& default_value) const {
return GetTargetValue<gfx::TransformOperations>(target_property,
default_value);
}
gfx::SizeF KeyframeEffect::GetTargetSizeValue(
int target_property,
const gfx::SizeF& default_value) const {
return GetTargetValue<gfx::SizeF>(target_property, default_value);
}
SkColor KeyframeEffect::GetTargetColorValue(int target_property,
SkColor default_value) const {
return GetTargetValue<SkColor>(target_property, default_value);
}
void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time,
bool include_infinite_animations) {
TargetProperties animated_properties;
for (auto& keyframe_model : keyframe_models_) {
if (!include_infinite_animations &&
keyframe_model->iterations() == std::numeric_limits<double>::infinity())
continue;
if (keyframe_model->run_state() == KeyframeModel::RUNNING ||
keyframe_model->run_state() == KeyframeModel::PAUSED) {
animated_properties[keyframe_model->TargetProperty()] = true;
}
}
for (auto& keyframe_model : keyframe_models_) {
if (!include_infinite_animations &&
keyframe_model->iterations() == std::numeric_limits<double>::infinity())
continue;
if (!animated_properties[keyframe_model->TargetProperty()] &&
keyframe_model->run_state() ==
KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY) {
animated_properties[keyframe_model->TargetProperty()] = true;
keyframe_model->SetRunState(KeyframeModel::RUNNING, monotonic_time);
keyframe_model->set_start_time(monotonic_time);
}
}
}
KeyframeModel* KeyframeEffect::GetRunningKeyframeModelForProperty(
int target_property) const {
for (auto& keyframe_model : keyframe_models_) {
if ((keyframe_model->run_state() == KeyframeModel::RUNNING ||
keyframe_model->run_state() == KeyframeModel::PAUSED) &&
keyframe_model->TargetProperty() == target_property) {
return keyframe_model.get();
}
}
return nullptr;
}
KeyframeModel* KeyframeEffect::GetKeyframeModel(int target_property) const {
for (size_t i = 0; i < keyframe_models().size(); ++i) {
size_t index = keyframe_models().size() - i - 1;
if (keyframe_models_[index]->TargetProperty() == target_property)
return keyframe_models_[index].get();
}
return nullptr;
}
KeyframeModel* KeyframeEffect::GetKeyframeModelById(int id) const {
for (auto& keyframe_model : keyframe_models())
if (keyframe_model->id() == id)
return keyframe_model.get();
return nullptr;
}
template <typename ValueType>
ValueType KeyframeEffect::GetTargetValue(int target_property,
const ValueType& default_value) const {
KeyframeModel* running_keyframe_model = GetKeyframeModel(target_property);
if (!running_keyframe_model) {
return default_value;
}
const auto* curve = AnimationTraits<ValueType>::ToDerivedCurve(
running_keyframe_model->curve());
return curve->GetValue(GetEndTime(running_keyframe_model));
}
} // namespace gfx