blob: 9a528e5509a553bc153c66a9011464bcc5ad9aff [file] [log] [blame]
// Copyright (c) 2012 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/compositor/layer_animation_element.h"
#include "base/compiler_specific.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_id_provider.h"
#include "ui/compositor/float_animation_curve_adapter.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_delegate.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/transform_animation_curve_adapter.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/interpolated_transform.h"
namespace ui {
namespace {
// The factor by which duration is scaled up or down when using
// ScopedAnimationDurationScaleMode.
const int kSlowDurationScaleMultiplier = 4;
const int kFastDurationScaleDivisor = 4;
const int kNonZeroDurationScaleDivisor = 20;
// Pause -----------------------------------------------------------------------
class Pause : public LayerAnimationElement {
public:
Pause(AnimatableProperties properties, base::TimeDelta duration)
: LayerAnimationElement(properties, duration) {
}
~Pause() override {}
private:
void OnStart(LayerAnimationDelegate* delegate) override {}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
return false;
}
void OnGetTarget(TargetValue* target) const override {}
void OnAbort(LayerAnimationDelegate* delegate) override {}
DISALLOW_COPY_AND_ASSIGN(Pause);
};
// TransformTransition ---------------------------------------------------------
class TransformTransition : public LayerAnimationElement {
public:
TransformTransition(const gfx::Transform& target, base::TimeDelta duration)
: LayerAnimationElement(TRANSFORM, duration),
target_(target) {
}
~TransformTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetTransformForAnimation();
}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
delegate->SetTransformFromAnimation(
gfx::Tween::TransformValueBetween(t, start_, target_));
return true;
}
void OnGetTarget(TargetValue* target) const override {
target->transform = target_;
}
void OnAbort(LayerAnimationDelegate* delegate) override {}
private:
gfx::Transform start_;
const gfx::Transform target_;
DISALLOW_COPY_AND_ASSIGN(TransformTransition);
};
// InterpolatedTransformTransition ---------------------------------------------
class InterpolatedTransformTransition : public LayerAnimationElement {
public:
InterpolatedTransformTransition(InterpolatedTransform* interpolated_transform,
base::TimeDelta duration)
: LayerAnimationElement(TRANSFORM, duration),
interpolated_transform_(interpolated_transform) {
}
~InterpolatedTransformTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
delegate->SetTransformFromAnimation(
interpolated_transform_->Interpolate(static_cast<float>(t)));
return true;
}
void OnGetTarget(TargetValue* target) const override {
target->transform = interpolated_transform_->Interpolate(1.0f);
}
void OnAbort(LayerAnimationDelegate* delegate) override {}
private:
scoped_ptr<InterpolatedTransform> interpolated_transform_;
DISALLOW_COPY_AND_ASSIGN(InterpolatedTransformTransition);
};
// BoundsTransition ------------------------------------------------------------
class BoundsTransition : public LayerAnimationElement {
public:
BoundsTransition(const gfx::Rect& target, base::TimeDelta duration)
: LayerAnimationElement(BOUNDS, duration),
target_(target) {
}
~BoundsTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetBoundsForAnimation();
}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
delegate->SetBoundsFromAnimation(
gfx::Tween::RectValueBetween(t, start_, target_));
return true;
}
void OnGetTarget(TargetValue* target) const override {
target->bounds = target_;
}
void OnAbort(LayerAnimationDelegate* delegate) override {}
private:
gfx::Rect start_;
const gfx::Rect target_;
DISALLOW_COPY_AND_ASSIGN(BoundsTransition);
};
// OpacityTransition -----------------------------------------------------------
class OpacityTransition : public LayerAnimationElement {
public:
OpacityTransition(float target, base::TimeDelta duration)
: LayerAnimationElement(OPACITY, duration),
start_(0.0f),
target_(target) {
}
~OpacityTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetOpacityForAnimation();
}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
delegate->SetOpacityFromAnimation(
gfx::Tween::FloatValueBetween(t, start_, target_));
return true;
}
void OnGetTarget(TargetValue* target) const override {
target->opacity = target_;
}
void OnAbort(LayerAnimationDelegate* delegate) override {}
private:
float start_;
const float target_;
DISALLOW_COPY_AND_ASSIGN(OpacityTransition);
};
// VisibilityTransition --------------------------------------------------------
class VisibilityTransition : public LayerAnimationElement {
public:
VisibilityTransition(bool target, base::TimeDelta duration)
: LayerAnimationElement(VISIBILITY, duration),
start_(false),
target_(target) {
}
~VisibilityTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetVisibilityForAnimation();
}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
delegate->SetVisibilityFromAnimation(t == 1.0 ? target_ : start_);
return t == 1.0;
}
void OnGetTarget(TargetValue* target) const override {
target->visibility = target_;
}
void OnAbort(LayerAnimationDelegate* delegate) override {}
private:
bool start_;
const bool target_;
DISALLOW_COPY_AND_ASSIGN(VisibilityTransition);
};
// BrightnessTransition --------------------------------------------------------
class BrightnessTransition : public LayerAnimationElement {
public:
BrightnessTransition(float target, base::TimeDelta duration)
: LayerAnimationElement(BRIGHTNESS, duration),
start_(0.0f),
target_(target) {
}
~BrightnessTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetBrightnessForAnimation();
}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
delegate->SetBrightnessFromAnimation(
gfx::Tween::FloatValueBetween(t, start_, target_));
return true;
}
void OnGetTarget(TargetValue* target) const override {
target->brightness = target_;
}
void OnAbort(LayerAnimationDelegate* delegate) override {}
private:
float start_;
const float target_;
DISALLOW_COPY_AND_ASSIGN(BrightnessTransition);
};
// GrayscaleTransition ---------------------------------------------------------
class GrayscaleTransition : public LayerAnimationElement {
public:
GrayscaleTransition(float target, base::TimeDelta duration)
: LayerAnimationElement(GRAYSCALE, duration),
start_(0.0f),
target_(target) {
}
~GrayscaleTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetGrayscaleForAnimation();
}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
delegate->SetGrayscaleFromAnimation(
gfx::Tween::FloatValueBetween(t, start_, target_));
return true;
}
void OnGetTarget(TargetValue* target) const override {
target->grayscale = target_;
}
void OnAbort(LayerAnimationDelegate* delegate) override {}
private:
float start_;
const float target_;
DISALLOW_COPY_AND_ASSIGN(GrayscaleTransition);
};
// ColorTransition -------------------------------------------------------------
class ColorTransition : public LayerAnimationElement {
public:
ColorTransition(SkColor target, base::TimeDelta duration)
: LayerAnimationElement(COLOR, duration),
start_(SK_ColorBLACK),
target_(target) {
}
~ColorTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetColorForAnimation();
}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
delegate->SetColorFromAnimation(
gfx::Tween::ColorValueBetween(t, start_, target_));
return true;
}
void OnGetTarget(TargetValue* target) const override {
target->color = target_;
}
void OnAbort(LayerAnimationDelegate* delegate) override {}
private:
SkColor start_;
const SkColor target_;
DISALLOW_COPY_AND_ASSIGN(ColorTransition);
};
// ThreadedLayerAnimationElement -----------------------------------------------
class ThreadedLayerAnimationElement : public LayerAnimationElement {
public:
ThreadedLayerAnimationElement(AnimatableProperties properties,
base::TimeDelta duration)
: LayerAnimationElement(properties, duration) {
}
~ThreadedLayerAnimationElement() override {}
bool IsThreaded() const override { return (duration() != base::TimeDelta()); }
protected:
explicit ThreadedLayerAnimationElement(const LayerAnimationElement& element)
: LayerAnimationElement(element) {
}
bool OnProgress(double t, LayerAnimationDelegate* delegate) override {
if (t < 1.0)
return false;
if (Started()) {
delegate->RemoveThreadedAnimation(animation_id());
}
OnEnd(delegate);
return true;
}
void OnAbort(LayerAnimationDelegate* delegate) override {
if (delegate && Started()) {
delegate->RemoveThreadedAnimation(animation_id());
}
}
void RequestEffectiveStart(LayerAnimationDelegate* delegate) override {
DCHECK(animation_group_id());
if (duration() == base::TimeDelta()) {
set_effective_start_time(requested_start_time());
return;
}
set_effective_start_time(base::TimeTicks());
scoped_ptr<cc::Animation> animation = CreateCCAnimation();
animation->set_needs_synchronized_start_time(true);
delegate->AddThreadedAnimation(animation.Pass());
}
virtual void OnEnd(LayerAnimationDelegate* delegate) = 0;
virtual scoped_ptr<cc::Animation> CreateCCAnimation() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(ThreadedLayerAnimationElement);
};
// ThreadedOpacityTransition ---------------------------------------------------
class ThreadedOpacityTransition : public ThreadedLayerAnimationElement {
public:
ThreadedOpacityTransition(float target, base::TimeDelta duration)
: ThreadedLayerAnimationElement(OPACITY, duration),
start_(0.0f),
target_(target) {
}
~ThreadedOpacityTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetOpacityForAnimation();
}
void OnAbort(LayerAnimationDelegate* delegate) override {
if (delegate && Started()) {
ThreadedLayerAnimationElement::OnAbort(delegate);
delegate->SetOpacityFromAnimation(gfx::Tween::FloatValueBetween(
gfx::Tween::CalculateValue(tween_type(), last_progressed_fraction()),
start_,
target_));
}
}
void OnEnd(LayerAnimationDelegate* delegate) override {
delegate->SetOpacityFromAnimation(target_);
}
scoped_ptr<cc::Animation> CreateCCAnimation() override {
scoped_ptr<cc::AnimationCurve> animation_curve(
new FloatAnimationCurveAdapter(tween_type(),
start_,
target_,
duration()));
scoped_ptr<cc::Animation> animation(
cc::Animation::Create(animation_curve.Pass(), animation_id(),
animation_group_id(), cc::Animation::OPACITY));
return animation.Pass();
}
void OnGetTarget(TargetValue* target) const override {
target->opacity = target_;
}
private:
float start_;
const float target_;
DISALLOW_COPY_AND_ASSIGN(ThreadedOpacityTransition);
};
// ThreadedTransformTransition -------------------------------------------------
class ThreadedTransformTransition : public ThreadedLayerAnimationElement {
public:
ThreadedTransformTransition(const gfx::Transform& target,
base::TimeDelta duration)
: ThreadedLayerAnimationElement(TRANSFORM, duration),
target_(target) {
}
~ThreadedTransformTransition() override {}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
start_ = delegate->GetTransformForAnimation();
}
void OnAbort(LayerAnimationDelegate* delegate) override {
if (delegate && Started()) {
ThreadedLayerAnimationElement::OnAbort(delegate);
delegate->SetTransformFromAnimation(gfx::Tween::TransformValueBetween(
gfx::Tween::CalculateValue(tween_type(), last_progressed_fraction()),
start_,
target_));
}
}
void OnEnd(LayerAnimationDelegate* delegate) override {
delegate->SetTransformFromAnimation(target_);
}
scoped_ptr<cc::Animation> CreateCCAnimation() override {
scoped_ptr<cc::AnimationCurve> animation_curve(
new TransformAnimationCurveAdapter(tween_type(),
start_,
target_,
duration()));
scoped_ptr<cc::Animation> animation(
cc::Animation::Create(animation_curve.Pass(), animation_id(),
animation_group_id(), cc::Animation::TRANSFORM));
return animation.Pass();
}
void OnGetTarget(TargetValue* target) const override {
target->transform = target_;
}
private:
gfx::Transform start_;
const gfx::Transform target_;
DISALLOW_COPY_AND_ASSIGN(ThreadedTransformTransition);
};
// InverseTransformTransision --------------------------------------------------
class InverseTransformTransition : public ThreadedLayerAnimationElement {
public:
InverseTransformTransition(const gfx::Transform& base_transform,
const LayerAnimationElement* uninverted_transition)
: ThreadedLayerAnimationElement(*uninverted_transition),
base_transform_(base_transform),
uninverted_transition_(
CheckAndCast<const ThreadedTransformTransition*>(
uninverted_transition)) {
}
~InverseTransformTransition() override {}
static InverseTransformTransition* Clone(const LayerAnimationElement* other) {
const InverseTransformTransition* other_inverse =
CheckAndCast<const InverseTransformTransition*>(other);
return new InverseTransformTransition(
other_inverse->base_transform_, other_inverse->uninverted_transition_);
}
protected:
void OnStart(LayerAnimationDelegate* delegate) override {
gfx::Transform start(delegate->GetTransformForAnimation());
effective_start_ = base_transform_ * start;
TargetValue target;
uninverted_transition_->GetTargetValue(&target);
base_target_ = target.transform;
set_tween_type(uninverted_transition_->tween_type());
TransformAnimationCurveAdapter base_curve(tween_type(),
base_transform_,
base_target_,
duration());
animation_curve_.reset(new InverseTransformCurveAdapter(
base_curve, start, duration()));
computed_target_transform_ = ComputeWithBaseTransform(effective_start_,
base_target_);
}
void OnAbort(LayerAnimationDelegate* delegate) override {
if (delegate && Started()) {
ThreadedLayerAnimationElement::OnAbort(delegate);
delegate->SetTransformFromAnimation(ComputeCurrentTransform());
}
}
void OnEnd(LayerAnimationDelegate* delegate) override {
delegate->SetTransformFromAnimation(computed_target_transform_);
}
scoped_ptr<cc::Animation> CreateCCAnimation() override {
scoped_ptr<cc::Animation> animation(
cc::Animation::Create(animation_curve_->Clone(), animation_id(),
animation_group_id(), cc::Animation::TRANSFORM));
return animation.Pass();
}
void OnGetTarget(TargetValue* target) const override {
target->transform = computed_target_transform_;
}
private:
gfx::Transform ComputeCurrentTransform() const {
gfx::Transform base_current = gfx::Tween::TransformValueBetween(
gfx::Tween::CalculateValue(tween_type(), last_progressed_fraction()),
base_transform_,
base_target_);
return ComputeWithBaseTransform(effective_start_, base_current);
}
gfx::Transform ComputeWithBaseTransform(gfx::Transform start,
gfx::Transform target) const {
gfx::Transform to_return(gfx::Transform::kSkipInitialization);
bool success = target.GetInverse(&to_return);
DCHECK(success) << "Target transform must be invertible.";
to_return.PreconcatTransform(start);
return to_return;
}
template <typename T>
static T CheckAndCast(const LayerAnimationElement* element) {
AnimatableProperties properties = element->properties();
DCHECK(properties & TRANSFORM);
return static_cast<T>(element);
}
gfx::Transform effective_start_;
gfx::Transform computed_target_transform_;
const gfx::Transform base_transform_;
gfx::Transform base_target_;
scoped_ptr<cc::AnimationCurve> animation_curve_;
const ThreadedTransformTransition* const uninverted_transition_;
DISALLOW_COPY_AND_ASSIGN(InverseTransformTransition);
};
} // namespace
// LayerAnimationElement::TargetValue ------------------------------------------
LayerAnimationElement::TargetValue::TargetValue()
: opacity(0.0f),
visibility(false),
brightness(0.0f),
grayscale(0.0f),
color(SK_ColorBLACK) {
}
LayerAnimationElement::TargetValue::TargetValue(
const LayerAnimationDelegate* delegate)
: bounds(delegate ? delegate->GetBoundsForAnimation() : gfx::Rect()),
transform(delegate ?
delegate->GetTransformForAnimation() : gfx::Transform()),
opacity(delegate ? delegate->GetOpacityForAnimation() : 0.0f),
visibility(delegate ? delegate->GetVisibilityForAnimation() : false),
brightness(delegate ? delegate->GetBrightnessForAnimation() : 0.0f),
grayscale(delegate ? delegate->GetGrayscaleForAnimation() : 0.0f),
color(delegate ? delegate->GetColorForAnimation() : 0.0f) {
}
// LayerAnimationElement -------------------------------------------------------
LayerAnimationElement::LayerAnimationElement(
AnimatableProperties properties, base::TimeDelta duration)
: first_frame_(true),
properties_(properties),
duration_(GetEffectiveDuration(duration)),
tween_type_(gfx::Tween::LINEAR),
animation_id_(cc::AnimationIdProvider::NextAnimationId()),
animation_group_id_(0),
last_progressed_fraction_(0.0),
weak_ptr_factory_(this) {
}
LayerAnimationElement::LayerAnimationElement(
const LayerAnimationElement &element)
: first_frame_(element.first_frame_),
properties_(element.properties_),
duration_(element.duration_),
tween_type_(element.tween_type_),
animation_id_(cc::AnimationIdProvider::NextAnimationId()),
animation_group_id_(element.animation_group_id_),
last_progressed_fraction_(element.last_progressed_fraction_),
weak_ptr_factory_(this) {
}
LayerAnimationElement::~LayerAnimationElement() {
}
void LayerAnimationElement::Start(LayerAnimationDelegate* delegate,
int animation_group_id) {
DCHECK(requested_start_time_ != base::TimeTicks());
DCHECK(first_frame_);
animation_group_id_ = animation_group_id;
last_progressed_fraction_ = 0.0;
OnStart(delegate);
RequestEffectiveStart(delegate);
first_frame_ = false;
}
bool LayerAnimationElement::Progress(base::TimeTicks now,
LayerAnimationDelegate* delegate) {
DCHECK(requested_start_time_ != base::TimeTicks());
DCHECK(!first_frame_);
bool need_draw;
double t = 1.0;
if ((effective_start_time_ == base::TimeTicks()) ||
(now < effective_start_time_)) {
// This hasn't actually started yet.
need_draw = false;
last_progressed_fraction_ = 0.0;
return need_draw;
}
base::TimeDelta elapsed = now - effective_start_time_;
if ((duration_ > base::TimeDelta()) && (elapsed < duration_))
t = elapsed.InMillisecondsF() / duration_.InMillisecondsF();
base::WeakPtr<LayerAnimationElement> alive(weak_ptr_factory_.GetWeakPtr());
need_draw = OnProgress(gfx::Tween::CalculateValue(tween_type_, t), delegate);
if (!alive)
return need_draw;
first_frame_ = t == 1.0;
last_progressed_fraction_ = t;
return need_draw;
}
bool LayerAnimationElement::IsFinished(base::TimeTicks time,
base::TimeDelta* total_duration) {
// If an effective start has been requested but the effective start time
// hasn't yet been set, the animation is not finished, regardless of the
// value of |time|.
if (!first_frame_ && (effective_start_time_ == base::TimeTicks()))
return false;
base::TimeDelta queueing_delay;
if (!first_frame_)
queueing_delay = effective_start_time_ - requested_start_time_;
base::TimeDelta elapsed = time - requested_start_time_;
if (elapsed >= duration_ + queueing_delay) {
*total_duration = duration_ + queueing_delay;
return true;
}
return false;
}
bool LayerAnimationElement::ProgressToEnd(LayerAnimationDelegate* delegate) {
if (first_frame_)
OnStart(delegate);
base::WeakPtr<LayerAnimationElement> alive(weak_ptr_factory_.GetWeakPtr());
bool need_draw = OnProgress(1.0, delegate);
if (!alive)
return need_draw;
last_progressed_fraction_ = 1.0;
first_frame_ = true;
return need_draw;
}
void LayerAnimationElement::GetTargetValue(TargetValue* target) const {
OnGetTarget(target);
}
bool LayerAnimationElement::IsThreaded() const {
return false;
}
void LayerAnimationElement::Abort(LayerAnimationDelegate* delegate) {
OnAbort(delegate);
first_frame_ = true;
}
void LayerAnimationElement::RequestEffectiveStart(
LayerAnimationDelegate* delegate) {
DCHECK(requested_start_time_ != base::TimeTicks());
effective_start_time_ = requested_start_time_;
}
// static
LayerAnimationElement::AnimatableProperty
LayerAnimationElement::ToAnimatableProperty(
cc::Animation::TargetProperty property) {
switch (property) {
case cc::Animation::TRANSFORM:
return TRANSFORM;
case cc::Animation::OPACITY:
return OPACITY;
default:
NOTREACHED();
return AnimatableProperty();
}
}
// static
base::TimeDelta LayerAnimationElement::GetEffectiveDuration(
const base::TimeDelta& duration) {
switch (ScopedAnimationDurationScaleMode::duration_scale_mode()) {
case ScopedAnimationDurationScaleMode::NORMAL_DURATION:
return duration;
case ScopedAnimationDurationScaleMode::FAST_DURATION:
return duration / kFastDurationScaleDivisor;
case ScopedAnimationDurationScaleMode::SLOW_DURATION:
return duration * kSlowDurationScaleMultiplier;
case ScopedAnimationDurationScaleMode::NON_ZERO_DURATION:
return duration / kNonZeroDurationScaleDivisor;
case ScopedAnimationDurationScaleMode::ZERO_DURATION:
return base::TimeDelta();
default:
NOTREACHED();
return base::TimeDelta();
}
}
// static
LayerAnimationElement* LayerAnimationElement::CreateTransformElement(
const gfx::Transform& transform,
base::TimeDelta duration) {
return new ThreadedTransformTransition(transform, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreateInverseTransformElement(
const gfx::Transform& base_transform,
const LayerAnimationElement* uninverted_transition) {
return new InverseTransformTransition(base_transform, uninverted_transition);
}
// static
LayerAnimationElement* LayerAnimationElement::CloneInverseTransformElement(
const LayerAnimationElement* other) {
return InverseTransformTransition::Clone(other);
}
// static
LayerAnimationElement*
LayerAnimationElement::CreateInterpolatedTransformElement(
InterpolatedTransform* interpolated_transform,
base::TimeDelta duration) {
return new InterpolatedTransformTransition(interpolated_transform, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreateBoundsElement(
const gfx::Rect& bounds,
base::TimeDelta duration) {
return new BoundsTransition(bounds, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreateOpacityElement(
float opacity,
base::TimeDelta duration) {
return new ThreadedOpacityTransition(opacity, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreateVisibilityElement(
bool visibility,
base::TimeDelta duration) {
return new VisibilityTransition(visibility, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreateBrightnessElement(
float brightness,
base::TimeDelta duration) {
return new BrightnessTransition(brightness, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreateGrayscaleElement(
float grayscale,
base::TimeDelta duration) {
return new GrayscaleTransition(grayscale, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreatePauseElement(
AnimatableProperties properties,
base::TimeDelta duration) {
return new Pause(properties, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreateColorElement(
SkColor color,
base::TimeDelta duration) {
return new ColorTransition(color, duration);
}
} // namespace ui