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