animation.rs

  1use crate::{ContentGroup, prelude::*};
  2use gpui::{AnimationElement, AnimationExt, Styled};
  3use std::time::Duration;
  4
  5use gpui::ease_out_quint;
  6
  7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
  8pub enum AnimationDuration {
  9    Instant = 50,
 10    Fast = 150,
 11    Slow = 300,
 12}
 13
 14impl AnimationDuration {
 15    pub fn duration(&self) -> Duration {
 16        Duration::from_millis(*self as u64)
 17    }
 18}
 19
 20impl Into<std::time::Duration> for AnimationDuration {
 21    fn into(self) -> Duration {
 22        self.duration()
 23    }
 24}
 25
 26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 27pub enum AnimationDirection {
 28    FromBottom,
 29    FromLeft,
 30    FromRight,
 31    FromTop,
 32}
 33
 34pub trait DefaultAnimations: Styled + Sized {
 35    fn animate_in(
 36        self,
 37        animation_type: AnimationDirection,
 38        fade_in: bool,
 39    ) -> AnimationElement<Self> {
 40        let animation_name = match animation_type {
 41            AnimationDirection::FromBottom => "animate_from_bottom",
 42            AnimationDirection::FromLeft => "animate_from_left",
 43            AnimationDirection::FromRight => "animate_from_right",
 44            AnimationDirection::FromTop => "animate_from_top",
 45        };
 46
 47        self.with_animation(
 48            animation_name,
 49            gpui::Animation::new(AnimationDuration::Fast.into()).with_easing(ease_out_quint()),
 50            move |mut this, delta| {
 51                let start_opacity = 0.4;
 52                let start_pos = 0.0;
 53                let end_pos = 40.0;
 54
 55                if fade_in {
 56                    this = this.opacity(start_opacity + delta * (1.0 - start_opacity));
 57                }
 58
 59                match animation_type {
 60                    AnimationDirection::FromBottom => {
 61                        this.bottom(px(start_pos + delta * (end_pos - start_pos)))
 62                    }
 63                    AnimationDirection::FromLeft => {
 64                        this.left(px(start_pos + delta * (end_pos - start_pos)))
 65                    }
 66                    AnimationDirection::FromRight => {
 67                        this.right(px(start_pos + delta * (end_pos - start_pos)))
 68                    }
 69                    AnimationDirection::FromTop => {
 70                        this.top(px(start_pos + delta * (end_pos - start_pos)))
 71                    }
 72                }
 73            },
 74        )
 75    }
 76
 77    fn animate_in_from_bottom(self, fade: bool) -> AnimationElement<Self> {
 78        self.animate_in(AnimationDirection::FromBottom, fade)
 79    }
 80
 81    fn animate_in_from_left(self, fade: bool) -> AnimationElement<Self> {
 82        self.animate_in(AnimationDirection::FromLeft, fade)
 83    }
 84
 85    fn animate_in_from_right(self, fade: bool) -> AnimationElement<Self> {
 86        self.animate_in(AnimationDirection::FromRight, fade)
 87    }
 88
 89    fn animate_in_from_top(self, fade: bool) -> AnimationElement<Self> {
 90        self.animate_in(AnimationDirection::FromTop, fade)
 91    }
 92}
 93
 94impl<E: Styled> DefaultAnimations for E {}
 95
 96// Don't use this directly, it only exists to show animation previews
 97#[derive(RegisterComponent)]
 98struct Animation {}
 99
100impl Component for Animation {
101    fn scope() -> ComponentScope {
102        ComponentScope::Utilities
103    }
104
105    fn description() -> Option<&'static str> {
106        Some("Demonstrates various animation patterns and transitions available in the UI system.")
107    }
108
109    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
110        let container_size = 128.0;
111        let element_size = 32.0;
112        let offset = container_size / 2.0 - element_size / 2.0;
113        Some(
114            v_flex()
115                .gap_6()
116                .children(vec![
117                    example_group_with_title(
118                        "Animate In",
119                        vec![
120                            single_example(
121                                "From Bottom",
122                                ContentGroup::new()
123                                    .relative()
124                                    .items_center()
125                                    .justify_center()
126                                    .size(px(container_size))
127                                    .child(
128                                        div()
129                                            .id("animate-in-from-bottom")
130                                            .absolute()
131                                            .size(px(element_size))
132                                            .left(px(offset))
133                                            .rounded_md()
134                                            .bg(gpui::red())
135                                            .animate_in(AnimationDirection::FromBottom, false),
136                                    )
137                                    .into_any_element(),
138                            ),
139                            single_example(
140                                "From Top",
141                                ContentGroup::new()
142                                    .relative()
143                                    .items_center()
144                                    .justify_center()
145                                    .size(px(container_size))
146                                    .child(
147                                        div()
148                                            .id("animate-in-from-top")
149                                            .absolute()
150                                            .size(px(element_size))
151                                            .left(px(offset))
152                                            .rounded_md()
153                                            .bg(gpui::blue())
154                                            .animate_in(AnimationDirection::FromTop, false),
155                                    )
156                                    .into_any_element(),
157                            ),
158                            single_example(
159                                "From Left",
160                                ContentGroup::new()
161                                    .relative()
162                                    .items_center()
163                                    .justify_center()
164                                    .size(px(container_size))
165                                    .child(
166                                        div()
167                                            .id("animate-in-from-left")
168                                            .absolute()
169                                            .size(px(element_size))
170                                            .top(px(offset))
171                                            .rounded_md()
172                                            .bg(gpui::green())
173                                            .animate_in(AnimationDirection::FromLeft, false),
174                                    )
175                                    .into_any_element(),
176                            ),
177                            single_example(
178                                "From Right",
179                                ContentGroup::new()
180                                    .relative()
181                                    .items_center()
182                                    .justify_center()
183                                    .size(px(container_size))
184                                    .child(
185                                        div()
186                                            .id("animate-in-from-right")
187                                            .absolute()
188                                            .size(px(element_size))
189                                            .top(px(offset))
190                                            .rounded_md()
191                                            .bg(gpui::yellow())
192                                            .animate_in(AnimationDirection::FromRight, false),
193                                    )
194                                    .into_any_element(),
195                            ),
196                        ],
197                    )
198                    .grow(),
199                    example_group_with_title(
200                        "Fade and Animate In",
201                        vec![
202                            single_example(
203                                "From Bottom",
204                                ContentGroup::new()
205                                    .relative()
206                                    .items_center()
207                                    .justify_center()
208                                    .size(px(container_size))
209                                    .child(
210                                        div()
211                                            .id("fade-animate-in-from-bottom")
212                                            .absolute()
213                                            .size(px(element_size))
214                                            .left(px(offset))
215                                            .rounded_md()
216                                            .bg(gpui::red())
217                                            .animate_in(AnimationDirection::FromBottom, true),
218                                    )
219                                    .into_any_element(),
220                            ),
221                            single_example(
222                                "From Top",
223                                ContentGroup::new()
224                                    .relative()
225                                    .items_center()
226                                    .justify_center()
227                                    .size(px(container_size))
228                                    .child(
229                                        div()
230                                            .id("fade-animate-in-from-top")
231                                            .absolute()
232                                            .size(px(element_size))
233                                            .left(px(offset))
234                                            .rounded_md()
235                                            .bg(gpui::blue())
236                                            .animate_in(AnimationDirection::FromTop, true),
237                                    )
238                                    .into_any_element(),
239                            ),
240                            single_example(
241                                "From Left",
242                                ContentGroup::new()
243                                    .relative()
244                                    .items_center()
245                                    .justify_center()
246                                    .size(px(container_size))
247                                    .child(
248                                        div()
249                                            .id("fade-animate-in-from-left")
250                                            .absolute()
251                                            .size(px(element_size))
252                                            .top(px(offset))
253                                            .rounded_md()
254                                            .bg(gpui::green())
255                                            .animate_in(AnimationDirection::FromLeft, true),
256                                    )
257                                    .into_any_element(),
258                            ),
259                            single_example(
260                                "From Right",
261                                ContentGroup::new()
262                                    .relative()
263                                    .items_center()
264                                    .justify_center()
265                                    .size(px(container_size))
266                                    .child(
267                                        div()
268                                            .id("fade-animate-in-from-right")
269                                            .absolute()
270                                            .size(px(element_size))
271                                            .top(px(offset))
272                                            .rounded_md()
273                                            .bg(gpui::yellow())
274                                            .animate_in(AnimationDirection::FromRight, true),
275                                    )
276                                    .into_any_element(),
277                            ),
278                        ],
279                    )
280                    .grow(),
281                ])
282                .into_any_element(),
283        )
284    }
285}