Skip to main content

sequoia_view/
transition.rs

1#![forbid(unsafe_code)]
2
3use sequoia_animation::{Animate, Easing, animate};
4use sequoia_core::Vec2;
5
6pub fn fade_in() -> Animate {
7    animate().opacity(0.0..1.0).build()
8}
9
10pub fn slide_in(dy: f32) -> Animate {
11    animate().opacity(0.0..1.0).translate_y(dy..0.0).build()
12}
13
14pub fn pop_in(dy: f32) -> Animate {
15    animate()
16        .opacity(0.0..1.0)
17        .translate_y(dy..0.0)
18        .ease(Easing::EaseOutBack)
19        .build()
20}
21
22#[derive(Clone, Debug, Default)]
23pub struct Sequence {
24    steps: Vec<(Animate, f32)>,
25}
26
27impl Sequence {
28    pub fn new() -> Self {
29        Self::default()
30    }
31
32    pub fn add(mut self, anim: Animate, at_secs: f32) -> Self {
33        self.steps.push((anim, at_secs.max(0.0)));
34        self
35    }
36
37    pub fn sample_at(&self, t_secs: f32) -> SequenceFrame {
38        let mut opacity: Option<f32> = None;
39        let mut offset: Option<Vec2> = None;
40        for (anim, at) in &self.steps {
41            let f = anim.sample_at((t_secs - at).max(0.0));
42            if f.opacity.is_some() {
43                opacity = f.opacity;
44            }
45            if f.offset.is_some() {
46                offset = f.offset;
47            }
48        }
49        SequenceFrame { opacity, offset }
50    }
51
52    pub fn is_running(&self, t_secs: f32) -> bool {
53        self.steps
54            .iter()
55            .any(|(a, at)| a.is_running((t_secs - at).max(0.0)))
56    }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq)]
60pub struct SequenceFrame {
61    pub opacity: Option<f32>,
62    pub offset: Option<Vec2>,
63}
64
65pub fn stagger(
66    n: usize,
67    each_delay_secs: f32,
68    make: impl Fn(usize) -> Animate,
69) -> Vec<(Animate, f32)> {
70    (0..n)
71        .map(|i| (make(i), i as f32 * each_delay_secs.max(0.0)))
72        .collect()
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn convenience_builders_cover_fade_slide_pop() {
81        let f = fade_in();
82        assert_eq!(f.sample_at(0.0).opacity, Some(0.0));
83        assert_eq!(f.sample_at(10.0).opacity, Some(1.0));
84        let s = slide_in(-8.0);
85        assert_eq!(s.sample_at(10.0).offset, Some(Vec2::ZERO));
86        let p = pop_in(12.0);
87        assert_eq!(p.sample_at(10.0).opacity, Some(1.0));
88    }
89
90    #[test]
91    fn sequence_layers_steps() {
92        use std::time::Duration;
93        let seq = Sequence::new()
94            .add(
95                animate()
96                    .opacity(0.0..1.0)
97                    .duration(Duration::from_millis(200))
98                    .build(),
99                0.0,
100            )
101            .add(
102                animate()
103                    .translate_y(-8.0..0.0)
104                    .duration(Duration::from_millis(200))
105                    .build(),
106                0.1,
107            );
108        let f = seq.sample_at(0.05);
109        assert!(f.opacity.is_some());
110
111        assert_eq!(f.offset, Some(Vec2::new(0.0, -8.0)));
112        assert!(seq.is_running(0.05));
113        assert!(!seq.is_running(10.0));
114    }
115
116    #[test]
117    fn stagger_offsets_starts() {
118        let steps = stagger(3, 0.05, |_| fade_in());
119        assert_eq!(steps.len(), 3);
120        assert_eq!(steps[1].1, 0.05);
121        assert_eq!(steps[2].1, 0.1);
122    }
123}