mod duration;
mod iteration;
mod mode;
mod utils;
pub use duration::{Delay, Duration};
pub use iteration::IterationCount;
pub use mode::Direction;
pub use lilt::Easing;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Timing {
duration: Duration,
delay: Delay,
direction: Direction,
easing: Easing,
iterations: IterationCount,
}
impl Timing {
#[must_use]
pub fn new(duration_ms: f64) -> Self {
Self {
duration: Duration::from_millis(duration_ms),
..Self::default()
}
}
#[must_use]
pub fn linear(duration_ms: f64) -> Self {
Self::new(duration_ms)
}
#[must_use]
pub fn ease_in(duration_ms: f64) -> Self {
Self::new(duration_ms).with_easing(Easing::EaseIn)
}
#[must_use]
pub fn ease_out(duration_ms: f64) -> Self {
Self::new(duration_ms).with_easing(Easing::EaseOut)
}
#[must_use]
pub fn ease_in_out(duration_ms: f64) -> Self {
Self::new(duration_ms).with_easing(Easing::EaseInOut)
}
#[must_use]
pub const fn duration(&self) -> Duration {
self.duration
}
#[must_use]
pub const fn delay(&self) -> Delay {
self.delay
}
#[must_use]
pub const fn direction(&self) -> Direction {
self.direction
}
#[must_use]
pub const fn easing(&self) -> Easing {
self.easing
}
#[must_use]
pub const fn iterations(&self) -> IterationCount {
self.iterations
}
#[must_use]
pub const fn with_delay(mut self, delay: Delay) -> Self {
self.delay = delay;
self
}
#[must_use]
pub const fn with_direction(mut self, direction: Direction) -> Self {
self.direction = direction;
self
}
#[must_use]
pub const fn with_easing(mut self, easing: Easing) -> Self {
self.easing = easing;
self
}
#[must_use]
pub fn with_iterations(mut self, iterations: impl Into<IterationCount>) -> Self {
self.iterations = iterations.into();
self
}
#[must_use]
pub fn active_duration(self) -> Option<Duration> {
let count = self.iterations.finite_count()?;
self.duration.checked_mul(count)
}
#[must_use]
pub fn total_duration(self) -> Option<Duration> {
let active = self.active_duration()?;
active.checked_add_delay(self.delay)
}
pub(crate) fn with_duration(mut self, duration: Duration) -> Self {
self.duration = duration;
self
}
pub(crate) fn with_rate(mut self, rate: f64) -> Self {
self.duration = self.duration.divided_by(rate);
self
}
}
impl Default for Timing {
fn default() -> Self {
Self {
duration: Duration::ZERO,
delay: Delay::ZERO,
direction: Direction::default(),
easing: Easing::Linear,
iterations: IterationCount::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::{Delay, Timing};
use float_cmp::assert_approx_eq;
#[test]
fn rate_scales_active_duration_without_changing_delay() {
let faster = Timing::new(200.0)
.with_delay(Delay::from_millis(40.0))
.with_iterations(3)
.with_rate(2.0);
let slower = Timing::new(200.0).with_rate(0.5);
assert_approx_eq!(f64, faster.duration().as_millis(), 100.0);
assert_approx_eq!(f64, faster.delay().as_millis(), 40.0);
assert_approx_eq!(f64, faster.total_duration().unwrap().as_millis(), 340.0);
assert_approx_eq!(f64, slower.duration().as_millis(), 400.0);
}
#[test]
fn invalid_rate_leaves_duration_unchanged() {
let timing = Timing::new(200.0);
assert_eq!(timing.with_rate(0.0).duration(), timing.duration());
assert_eq!(timing.with_rate(-1.0).duration(), timing.duration());
assert_eq!(timing.with_rate(f64::NAN).duration(), timing.duration());
assert_eq!(
timing.with_rate(f64::INFINITY).duration(),
timing.duration()
);
}
}