1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! # A library for creating animations and transitions in Rust.
//!
//! The lib contains two main types: [`Animation`] and [`InertialValue`].
//! * [`Animation`] can be used in cases when we know start, end, and in between keyframes.
//! * [`InertialValue`] can be used to make an object smoothly follow a target value.
//! For example, a particle following a cursor. Background color changing smoothly on theme change.
//!
//! It also contains a set of easing functions to make animations more natural. See the [`Easing`] enum for more details.
//!
//! Most of the methods receive `Instant` as a parameter to allow testing without mocks,
//! and have a consistent behavior during a single animation frame. It's expected that time is received
//! from `Instant::now()` once in the beginning of the frame, and used lately during the frame rendering.
//!
//! Animation can be applied to any type that implements [`Mix`] trait. This trait is used to interpolate between two values.
//! Mix trait is implemented for common types like `f32`, `f64`, `bool`, `i8` - `i64`, `u8` - `u64`, `Option<T: Mix>`,
//! and tuples like `(Mix, Mix)`, `(Mix, Mix, Mix)`, etc. It's also implemented for some popular libraries:
//! [`nalgebra`](https://crates.io/crates/nalgebra), [`euclid`](https://crates.io/crates/euclid),
//! [cgmath](https://crates.io/crates/cgmath), and [`palette`](https://crates.io/crates/palette).
//! To make it work, you need to enable the corresponding feature.
//!
//! # Derive macro
//!
//! The library contains a derive macro to implement the `Mix` trait for structs and tuples.
//! ```
//! # #[cfg(feature = "derive")]
//! use glissade::Mix;
//!
//! # #[cfg(feature = "derive")]
//! #[derive(Mix, PartialEq, Debug)]
//! struct Touch {
//! x: f32,
//! y: f32,
//! pressure: u8,
//! }
//!
//! # #[cfg(feature = "derive")]
//! {
//! let touch1 = Touch { x: 0.0, y: 0.0, pressure: 0 };
//! let touch2 = Touch { x: 100.0, y: 100.0, pressure: 200 };
//! let touch_mix = touch1.mix(touch2, 0.5);
//! assert_eq!(touch_mix, Touch { x: 50.0, y: 50.0, pressure: 100 });
//! }
//! ```
//!
//! # Examples
//!
//! ## Simple two-step animation
//!
//! ```
//! use glissade::{Easing, transition, Transition};
//! use web_time::{Duration, Instant};
//!
//! // Create an animation template - a transition.
//! //
//! // This transition consists of two steps:
//! // 1. from 0.0 to 10.0 in 1 second linearly,
//! // 2. and then go to 5.0 with easing function.
//! let transition = transition(0.0)
//! .go_to(10.0, Duration::from_secs(1))
//! .ease_to(5.0, Duration::from_secs(2), Easing::QuadraticInOut);
//!
//! let now = Instant::now();
//! // Create an animation from the transition and start time.
//! let animation = transition.run(now);
//!
//! assert_eq!(animation.get(now), 0.0);
//! assert_eq!(animation.get(now + Duration::from_millis(500)), 5.0);
//! assert_eq!(animation.get(now + Duration::from_secs(1)), 10.0);
//! assert_eq!(animation.get(now + Duration::from_secs(2)), 7.5);
//! assert_eq!(animation.get(now + Duration::from_secs(3)), 5.0);
//!```
//!
//! ## Smoothly change color
//!
//!```
//! use glissade::{InertialValue, Easing};
//! use web_time::{Duration, Instant};
//!
//! type Color = (f32, f32, f32);
//!
//! let start_time = Instant::now();
//!
//! // Create initial black value
//! let value: InertialValue<Color> = InertialValue::new((0.0, 0.0, 0.0));
//!
//! assert_eq!(value.get(start_time), (0.0, 0.0, 0.0));
//! assert_eq!(value.get(start_time + Duration::from_secs(1)), (0.0, 0.0, 0.0));
//!
//! // Change color to white in one second
//! let value = value.go_to((1.0, 1.0, 1.0), start_time, Duration::from_secs(1));
//!
//! assert_eq!(value.get(start_time), (0.0, 0.0, 0.0));
//! assert_eq!(value.get(start_time + Duration::from_millis(500)), (0.5, 0.5, 0.5));
//! assert_eq!(value.get(start_time + Duration::from_secs(1)), (1.0, 1.0, 1.0));
//! assert_eq!(value.get(start_time + Duration::from_secs(2)), (1.0, 1.0, 1.0));
//!
//! // Change color to red in between the transition
//! let value = value.ease_to((1.0, 0.0, 0.0), start_time + Duration::from_millis(500), Duration::from_secs(2), Easing::Linear);
//!
//! assert_eq!(value.get(start_time + Duration::from_millis(500)), (0.5, 0.5, 0.5));
//! assert_eq!(value.get(start_time + Duration::from_secs(1)), (1.0, 0.75, 0.75));
//! assert_eq!(value.get(start_time + Duration::from_secs(2)), (1.0, 0.25, 0.25));
//! assert_eq!(value.get(start_time + Duration::from_millis(2500)), (1.0, 0.0, 0.0));
//! assert_eq!(value.get(start_time + Duration::from_secs(4)), (1.0, 0.0, 0.0));
//! ```
mod animation;
mod easing;
mod inertial_value;
mod mix;
mod transition;
mod transition_item;
#[cfg(feature = "cgmath")]
mod cgmath;
#[cfg(feature = "euclid")]
mod euclid;
#[cfg(feature = "nalgebra")]
mod nalgebra;
#[cfg(feature = "palette")]
mod palette;
pub use animation::Animation;
pub use easing::Easing;
#[cfg(feature = "derive")]
pub use glissade_macro::Mix;
pub use inertial_value::InertialValue;
pub use mix::Mix;
pub use transition::{transition, Transition};
#[cfg(test)]
#[cfg(feature = "derive")]
mod tests {
use crate as glissade;
use crate::Mix;
#[derive(Mix, PartialEq, Debug)]
struct Point {
x: f32,
y: f32,
}
#[test]
fn test_struct_derive() {
let p1 = Point { x: 0.0, y: 0.0 };
let p2 = Point { x: 1.0, y: 1.0 };
let p3 = p1.mix(p2, 0.5);
assert_eq!(p3, Point { x: 0.5, y: 0.5 });
}
#[derive(Mix, PartialEq, Debug)]
struct Color(f32, f32, f32);
#[test]
fn test_tuple_derive() {
let c1 = Color(0.0, 0.0, 0.0);
let c2 = Color(1.0, 1.0, 1.0);
let c3 = c1.mix(c2, 0.5);
assert_eq!(c3, Color(0.5, 0.5, 0.5));
}
#[derive(Mix, PartialEq, Debug)]
struct Size<T: Mix>
where
T: Clone + Copy,
{
width: T,
height: T,
}
#[test]
fn test_generics_derive() {
let s1 = Size {
width: 0.0,
height: 0.0,
};
let s2 = Size {
width: 1.0,
height: 1.0,
};
let s3 = s1.mix(s2, 0.5);
assert_eq!(
s3,
Size {
width: 0.5,
height: 0.5
}
);
}
}