whisker_css/shorthand/
animation.rs1use core::fmt;
5
6use crate::css::Css;
7use crate::data_type::Time;
8use crate::data_type_ext::EasingFunction;
9use crate::keyword::{
10 AnimationDirection, AnimationFillMode, AnimationIterationCount, AnimationPlayState,
11};
12use crate::to_css::ToCss;
13
14#[derive(Clone, Debug, PartialEq)]
16pub struct Animation {
17 pub name: String,
19 pub duration: Option<Time>,
21 pub timing: Option<EasingFunction>,
23 pub delay: Option<Time>,
25 pub iteration_count: Option<AnimationIterationCount>,
27 pub direction: Option<AnimationDirection>,
29 pub fill_mode: Option<AnimationFillMode>,
31 pub play_state: Option<AnimationPlayState>,
33}
34
35impl Animation {
36 pub fn new(name: impl Into<String>) -> Self {
38 Self {
39 name: name.into(),
40 duration: None,
41 timing: None,
42 delay: None,
43 iteration_count: None,
44 direction: None,
45 fill_mode: None,
46 play_state: None,
47 }
48 }
49
50 pub fn duration(mut self, d: Time) -> Self {
52 self.duration = Some(d);
53 self
54 }
55
56 pub fn timing(mut self, t: EasingFunction) -> Self {
58 self.timing = Some(t);
59 self
60 }
61
62 pub fn delay(mut self, d: Time) -> Self {
64 self.delay = Some(d);
65 self
66 }
67
68 pub fn iteration_count(mut self, c: AnimationIterationCount) -> Self {
70 self.iteration_count = Some(c);
71 self
72 }
73
74 pub fn direction(mut self, d: AnimationDirection) -> Self {
76 self.direction = Some(d);
77 self
78 }
79
80 pub fn fill_mode(mut self, f: AnimationFillMode) -> Self {
82 self.fill_mode = Some(f);
83 self
84 }
85
86 pub fn play_state(mut self, p: AnimationPlayState) -> Self {
88 self.play_state = Some(p);
89 self
90 }
91}
92
93impl ToCss for Animation {
94 fn to_css(&self, dest: &mut dyn fmt::Write) -> fmt::Result {
95 dest.write_str(&self.name)?;
96 if let Some(d) = &self.duration {
99 dest.write_char(' ')?;
100 d.to_css(dest)?;
101 }
102 if let Some(t) = &self.timing {
103 dest.write_char(' ')?;
104 t.to_css(dest)?;
105 }
106 if let Some(d) = &self.delay {
107 dest.write_char(' ')?;
108 d.to_css(dest)?;
109 }
110 if let Some(c) = &self.iteration_count {
111 dest.write_char(' ')?;
112 c.to_css(dest)?;
113 }
114 if let Some(d) = &self.direction {
115 dest.write_char(' ')?;
116 d.to_css(dest)?;
117 }
118 if let Some(f) = &self.fill_mode {
119 dest.write_char(' ')?;
120 f.to_css(dest)?;
121 }
122 if let Some(p) = &self.play_state {
123 dest.write_char(' ')?;
124 p.to_css(dest)?;
125 }
126 Ok(())
127 }
128}
129
130impl Css {
131 pub fn animation(self, a: Animation) -> Self {
134 self.push("animation", a)
135 }
136
137 pub fn animations(self, anims: impl IntoIterator<Item = Animation>) -> Self {
140 let mut s = String::new();
141 for (i, a) in anims.into_iter().enumerate() {
142 if i > 0 {
143 s.push_str(", ");
144 }
145 let _ = a.to_css(&mut s);
146 }
147 self.push_raw("animation", s)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use crate::data_type_ext::EasingFunction;
154 use crate::ext::*;
155 use crate::keyword::*;
156 use crate::Css;
157
158 use super::*;
159
160 #[test]
161 fn animation_name_only() {
162 let s = Css::new().animation(Animation::new("spin"));
163 assert_eq!(s.to_string(), "animation: spin;");
164 }
165
166 #[test]
167 fn animation_full_shorthand() {
168 let s = Css::new().animation(
169 Animation::new("spin")
170 .duration(1.s())
171 .timing(EasingFunction::Linear)
172 .delay(100.ms())
173 .iteration_count(AnimationIterationCount::Infinite)
174 .direction(AnimationDirection::Alternate)
175 .fill_mode(AnimationFillMode::Forwards)
176 .play_state(AnimationPlayState::Running),
177 );
178 assert_eq!(
179 s.to_string(),
180 "animation: spin 1s linear 100ms infinite alternate forwards running;"
181 );
182 }
183
184 #[test]
185 fn animations_multiple() {
186 let s = Css::new().animations([
187 Animation::new("fade").duration(300.ms()),
188 Animation::new("slide").duration(500.ms()).delay(100.ms()),
189 ]);
190 assert_eq!(s.to_string(), "animation: fade 300ms, slide 500ms 100ms;");
191 }
192}