blob: 3cb053b81368f0002c62a1c9f9170c783547a48f [file] [log] [blame]
// Copyright 2021 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.
#ifndef UI_GFX_ANIMATION_KEYFRAME_KEYFRAME_MODEL_H_
#define UI_GFX_ANIMATION_KEYFRAME_KEYFRAME_MODEL_H_
#include <string>
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/animation/keyframe/animation_curve.h"
#include "ui/gfx/animation/keyframe/keyframe_animation_export.h"
#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h"
namespace gfx {
class GFX_KEYFRAME_ANIMATION_EXPORT KeyframeModel {
public:
// KeyframeModels begin in the 'WAITING_FOR_TARGET_AVAILABILITY' state. A
// KeyframeModel waiting for target availability will run as soon as its
// target property is free (and all the KeyframeModels animating with it are
// also able to run). When this time arrives, the controller will move the
// keyframe model into the STARTING state, and then into the RUNNING state.
// RUNNING KeyframeModels may toggle between RUNNING and PAUSED, and may be
// stopped by moving into either the ABORTED or FINISHED states. A FINISHED
// keyframe model was allowed to run to completion, but an ABORTED keyframe
// model was not. An animation in the state ABORTED_BUT_NEEDS_COMPLETION is a
// keyframe model that was aborted for some reason, but needs to be finished.
// Currently this is for impl-only scroll offset KeyframeModels that need to
// be completed on the main thread.
enum RunState {
WAITING_FOR_TARGET_AVAILABILITY = 0,
WAITING_FOR_DELETION,
STARTING,
RUNNING,
PAUSED,
FINISHED,
ABORTED,
ABORTED_BUT_NEEDS_COMPLETION,
// This sentinel must be last.
LAST_RUN_STATE = ABORTED_BUT_NEEDS_COMPLETION
};
static std::string ToString(RunState);
enum class Direction { NORMAL, REVERSE, ALTERNATE_NORMAL, ALTERNATE_REVERSE };
enum class FillMode { NONE, FORWARDS, BACKWARDS, BOTH, AUTO };
enum class Phase { BEFORE, ACTIVE, AFTER };
static std::unique_ptr<KeyframeModel> Create(
std::unique_ptr<AnimationCurve> curve,
int keyframe_model_id,
int target_property_id);
KeyframeModel(const KeyframeModel&) = delete;
virtual ~KeyframeModel();
KeyframeModel& operator=(const KeyframeModel&) = delete;
int id() const { return id_; }
virtual int TargetProperty() const;
RunState run_state() const { return run_state_; }
virtual void SetRunState(RunState run_state, base::TimeTicks monotonic_time);
// Pause the keyframe effect at local time |pause_offset|.
void Pause(base::TimeDelta pause_offset);
base::TimeTicks start_time() const { return start_time_; }
void set_start_time(base::TimeTicks monotonic_time) {
start_time_ = monotonic_time;
}
bool has_set_start_time() const { return !start_time_.is_null(); }
base::TimeDelta time_offset() const { return time_offset_; }
void set_time_offset(base::TimeDelta monotonic_time) {
time_offset_ = monotonic_time;
}
Direction direction() const { return direction_; }
void set_direction(Direction direction) { direction_ = direction; }
FillMode fill_mode() const { return fill_mode_; }
void set_fill_mode(FillMode fill_mode) { fill_mode_ = fill_mode; }
double playback_rate() const { return playback_rate_; }
void set_playback_rate(double playback_rate) {
playback_rate_ = playback_rate;
}
base::TimeTicks pause_time() const { return pause_time_; }
void set_pause_time(base::TimeTicks pause_time) { pause_time_ = pause_time; }
base::TimeDelta total_paused_duration() const {
return total_paused_duration_;
}
void set_total_paused_duration(base::TimeDelta total_paused_duration) {
total_paused_duration_ = total_paused_duration;
}
// This is the number of times that the keyframe model will play. If this
// value is zero the keyframe model will not play. If it is negative, then
// the keyframe model will loop indefinitely.
double iterations() const { return iterations_; }
void set_iterations(double n) { iterations_ = n; }
double iteration_start() const { return iteration_start_; }
void set_iteration_start(double iteration_start) {
iteration_start_ = iteration_start;
}
AnimationCurve* curve() { return curve_.get(); }
const AnimationCurve* curve() const { return curve_.get(); }
bool IsFinishedAt(base::TimeTicks monotonic_time) const;
bool is_finished() const {
return run_state_ == FINISHED || run_state_ == ABORTED ||
run_state_ == WAITING_FOR_DELETION;
}
bool HasActiveTime(base::TimeTicks monotonic_time) const;
template <typename T>
void Retarget(base::TimeTicks now,
int property_id,
const T& new_target_value) {
if (!curve_)
return;
base::TimeDelta now_delta = TrimTimeToCurrentIteration(now);
DCHECK_EQ(CalculatePhase(now_delta), KeyframeModel::Phase::ACTIVE);
auto* keyframed_curve = AnimationTraits<T>::ToKeyframedCurve(curve_.get());
DCHECK(keyframed_curve);
if (auto new_curve = keyframed_curve->Retarget(now_delta, new_target_value))
curve_ = std::move(new_curve);
}
// Some clients may run threaded animations and may need to defer starting
// until the animation on the other thread has been started.
virtual bool StartShouldBeDeferred() const;
// Takes the given absolute time, and using the start time and the number
// of iterations, returns the relative time in the current iteration.
base::TimeDelta TrimTimeToCurrentIteration(
base::TimeTicks monotonic_time) const;
KeyframeModel::Phase CalculatePhaseForTesting(
base::TimeDelta local_time) const;
protected:
KeyframeModel(std::unique_ptr<AnimationCurve> curve,
int keyframe_model_id,
int target_property_id);
void ForceRunState(RunState run_state) { run_state_ = run_state; }
absl::optional<base::TimeDelta> CalculateActiveTime(
base::TimeTicks monotonic_time) const;
private:
KeyframeModel::Phase CalculatePhase(base::TimeDelta local_time) const;
// Return local time for this keyframe model given the absolute monotonic
// time.
//
// Local time represents the time value that is used to tick this keyframe
// model and is relative to its start time. It is closely related to the local
// time concept in web animations [1]. It is:
// - for playing animation : wall time - start time - paused duration
// - for paused animation : paused time
// - otherwise : zero
//
// Here is small diagram that shows how active, local, and monotonic times
// relate to each other and to the run state.
//
// run state Starting (R)unning Paused (R) Paused (R) Finished
// ^ ^
// | |
// monotonic time ------------------------------------------------->
// | |
// local time +-----------------+ +---+ +--------->
// | |
// active time + +------+ +---+ +------+
// (-offset)
//
// [1] https://drafts.csswg.org/web-animations/#local-time-section
base::TimeDelta ConvertMonotonicTimeToLocalTime(
base::TimeTicks monotonic_time) const;
std::unique_ptr<AnimationCurve> curve_;
// IDs must be unique.
int id_;
int target_property_ = 0;
RunState run_state_;
double iterations_;
double iteration_start_;
Direction direction_;
double playback_rate_;
FillMode fill_mode_;
base::TimeTicks start_time_;
// The time offset effectively pushes the start of the keyframe model back in
// time. This is used for resuming paused KeyframeModels -- an animation is
// added with a non-zero time offset, causing the keyframe model to skip ahead
// to the desired point in time.
base::TimeDelta time_offset_;
// These are used when converting monotonic to local time to account for time
// spent while paused. This is not included in AnimationState since it
// there is absolutely no need for clients of this controller to know
// about these values.
base::TimeTicks pause_time_;
base::TimeDelta total_paused_duration_;
};
} // namespace gfx
#endif // UI_GFX_ANIMATION_KEYFRAME_KEYFRAME_MODEL_H_