immi/animations/
mod.rs

1// Copyright 2016 immi Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! Contains everything related to the animations that are supported by this library.
9
10use std::time::Duration;
11use std::time::SystemTime;
12
13/// Describes how an animation should be interpolated.
14pub trait Interpolation {
15    /// Takes a number representing the number of animation cycles that have elapsed, and returns
16    /// the progress of the interpolation.
17    ///
18    /// Note that we're using `f64` instead of `f32` like in the rest of the library, because it
19    /// is common to start an animation at `UNIX_EPOCH` which is far away enough to cause precision
20    /// issues.
21    fn from_progress(&self, anim_progress: f64) -> f64;
22
23    /// Takes an instance representing the current point in time, an instant representing the
24    /// point in time when the animation has started or will start, the duration, and returns a
25    /// value between 0.0 and 1.0 representing the progress of the animation.
26    ///
27    /// Implementations typically return `0.0` when `now < start` and `1.0` when
28    /// `now > start + duration_ns`.
29    fn calculate(&self, now: SystemTime, start: SystemTime, duration: Duration) -> f64 {
30        let now_minus_start_ms = {
31            let (dur, neg) = match now.duration_since(start) {
32                Ok(d) => (d, false),
33                Err(err) => (err.duration(), true)
34            };
35
36            let val = dur.as_secs() as f64 * 1000000.0 + dur.subsec_nanos() as f64 / 1000.0;
37            if neg { -val } else { val }
38        };
39
40        let duration_ms = duration.as_secs() as f64 * 1000000.0 +
41                          duration.subsec_nanos() as f64 / 1000.0;
42
43        let anim_progress = now_minus_start_ms / duration_ms;
44        self.from_progress(anim_progress)
45    }
46
47    /// Reverses an interpolation. The element will start at its final position and go towards
48    /// the start.
49    #[inline]
50    fn reverse(self) -> Reversed<Self> where Self: Sized {
51        Reversed::new(self)
52    }
53
54    /// Repeats an interpolation forever.
55    #[inline]
56    fn repeat(self) -> Repeated<Self> where Self: Sized {
57        Repeated::new(self)
58    }
59
60    /// Repeats an interpolation forever. Every other cycle is reversed.
61    #[inline]
62    fn alternate_repeat(self) -> AlternateRepeated<Self> where Self: Sized {
63        AlternateRepeated::new(self)
64    }
65}
66
67/// A linear animation. The animation progresses at a constant rate.
68#[derive(Copy, Clone, Default, Debug)]
69pub struct Linear;
70
71impl Interpolation for Linear {
72    #[inline]
73    fn from_progress(&self, anim_progress: f64) -> f64 {
74        if anim_progress >= 1.0 {
75            1.0
76        } else if anim_progress <= 0.0 {
77            0.0
78        } else {
79            anim_progress
80        }
81    }
82}
83
84/// An ease-out animation. The animation progresses quickly and then slows down before reaching its
85/// final position.
86#[derive(Copy, Clone, Debug)]
87pub struct EaseOut {
88    /// The formula is `1.0 - exp(-linear_progress * factor)`.
89    ///
90    /// The higher the factor, the quicker the element will reach its destination.
91    pub factor: f64,
92}
93
94impl EaseOut {
95    /// Builds a `EaseOut` object.
96    #[inline]
97    pub fn new(factor: f64) -> EaseOut {
98        EaseOut {
99            factor: factor,
100        }
101    }
102}
103
104impl Default for EaseOut {
105    #[inline]
106    fn default() -> EaseOut {
107        EaseOut { factor: 10.0 }
108    }
109}
110
111impl Interpolation for EaseOut {
112    #[inline]
113    fn from_progress(&self, anim_progress: f64) -> f64 {
114        1.0 - (-anim_progress * self.factor).exp()
115    }
116}
117
118/// Wraps around an interpolation and reverses it. The element will start at its final position
119/// and go towards the start.
120#[derive(Copy, Clone, Debug)]
121pub struct Reversed<I> {
122    inner: I
123}
124
125impl<I> Reversed<I> where I: Interpolation {
126    /// Builds a `Reversed` object.
127    #[inline]
128    pub fn new(inner: I) -> Reversed<I> {
129        Reversed {
130            inner: inner,
131        }
132    }
133}
134
135impl<I> Interpolation for Reversed<I> where I: Interpolation {
136    #[inline]
137    fn from_progress(&self, anim_progress: f64) -> f64 {
138        self.inner.from_progress(1.0 - anim_progress)
139    }
140}
141
142/// Wraps around an interpolation and repeats the interpolation multiple times.
143#[derive(Copy, Clone, Debug)]
144pub struct Repeated<I> {
145    inner: I
146}
147
148impl<I> Repeated<I> where I: Interpolation {
149    /// Builds a `Repeated` object.
150    #[inline]
151    pub fn new(inner: I) -> Repeated<I> {
152        Repeated {
153            inner: inner,
154        }
155    }
156}
157
158impl<I> Interpolation for Repeated<I> where I: Interpolation {
159    #[inline]
160    fn from_progress(&self, anim_progress: f64) -> f64 {
161        let progress = if anim_progress < 0.0 { 1.0 + anim_progress % 1.0 }
162                       else { anim_progress % 1.0 };
163        self.inner.from_progress(progress)
164    }
165}
166
167/// Wraps around an interpolation and repeats the interpolation multiple times. Each uneven cycle
168/// the animation is reversed.
169#[derive(Copy, Clone, Debug)]
170pub struct AlternateRepeated<I> {
171    inner: I
172}
173
174impl<I> AlternateRepeated<I> where I: Interpolation {
175    /// Builds a `AlternateRepeated` object.
176    #[inline]
177    pub fn new(inner: I) -> AlternateRepeated<I> {
178        AlternateRepeated {
179            inner: inner,
180        }
181    }
182}
183
184impl<I> Interpolation for AlternateRepeated<I> where I: Interpolation {
185    #[inline]
186    fn from_progress(&self, anim_progress: f64) -> f64 {
187        let progress = 1.0 - ((anim_progress.abs() % 2.0) - 1.0).abs();
188        self.inner.from_progress(progress)
189    }
190}