blob: c66bb01394d5703970ffa6c5eab0afc67cd97eb3 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::{
animation::{LinearAnimation, Loop},
artboard::Artboard,
core::Object,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Direction {
Forwards,
Backwards,
}
impl From<Direction> for f32 {
fn from(direction: Direction) -> Self {
match direction {
Direction::Forwards => 1.0,
Direction::Backwards => -1.0,
}
}
}
#[derive(Clone, Debug)]
pub struct LinearAnimationInstance {
linear_animation: Object<LinearAnimation>,
time: f32,
direction: Direction,
did_loop: bool,
}
impl LinearAnimationInstance {
pub fn new(linear_animation: Object<LinearAnimation>) -> Self {
let time = linear_animation
.as_ref()
.enable_work_area()
.then(|| {
linear_animation.as_ref().work_start() as f32
/ linear_animation.as_ref().fps() as f32
})
.unwrap_or_default();
Self { linear_animation, time, direction: Direction::Forwards, did_loop: false }
}
pub fn is_done(&self) -> bool {
self.linear_animation.as_ref().r#loop() == Loop::OneShot && self.did_loop
}
pub fn reset(&mut self) {
*self = Self::new(self.linear_animation.clone());
}
pub fn set_time(&mut self, time: f32) {
if time == self.time {
return;
}
self.time = time;
self.direction = Direction::Forwards;
}
pub fn advance(&mut self, elapsed_seconds: f32) -> bool {
let linear_animation = self.linear_animation.as_ref();
let direction: f32 = self.direction.into();
self.time += elapsed_seconds * linear_animation.speed() * direction;
let fps = linear_animation.fps() as f32;
let mut frames = self.time * fps;
let start = linear_animation
.enable_work_area()
.then(|| linear_animation.work_start() as f32)
.unwrap_or_default();
let end = linear_animation
.enable_work_area()
.then(|| linear_animation.work_end() as f32)
.unwrap_or_else(|| linear_animation.duration() as f32);
let range = end - start;
self.did_loop = false;
let mut keep_going = true;
match linear_animation.r#loop() {
Loop::OneShot => {
if frames > end {
keep_going = false;
frames = end;
self.time = frames / fps;
self.did_loop = true;
}
}
Loop::Loop => {
if frames >= end {
frames = start + (self.time * fps - start) % range;
self.time = frames / fps;
self.did_loop = true;
}
}
Loop::PingPong => loop {
if self.direction == Direction::Forwards && frames >= end {
self.direction = Direction::Backwards;
frames = end + (end - frames);
self.time = frames / fps;
self.did_loop = true;
} else if self.direction == Direction::Backwards && frames < start {
self.direction = Direction::Forwards;
frames = start + (start - frames);
self.time = frames / fps;
self.did_loop = true;
} else {
break;
}
},
}
keep_going
}
pub fn apply(&self, artboard: Object<Artboard>, mix: f32) {
self.linear_animation.as_ref().apply(artboard, self.time, mix);
}
}