sequoia_view/
transition.rs1#![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}