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(IntoComponent)]
 98struct Animation {}
 99
100// View this component preview using `workspace: open component-preview`
101impl ComponentPreview for Animation {
102    fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
103        let container_size = 128.0;
104        let element_size = 32.0;
105        let left_offset = element_size - container_size / 2.0;
106        v_flex()
107            .gap_6()
108            .children(vec![
109                example_group_with_title(
110                    "Animate In",
111                    vec![
112                        single_example(
113                            "From Bottom",
114                            ContentGroup::new()
115                                .relative()
116                                .items_center()
117                                .justify_center()
118                                .size(px(container_size))
119                                .child(
120                                    div()
121                                        .id("animate-in-from-bottom")
122                                        .absolute()
123                                        .size(px(element_size))
124                                        .left(px(left_offset))
125                                        .rounded_md()
126                                        .bg(gpui::red())
127                                        .animate_in(AnimationDirection::FromBottom, false),
128                                )
129                                .into_any_element(),
130                        ),
131                        single_example(
132                            "From Top",
133                            ContentGroup::new()
134                                .relative()
135                                .items_center()
136                                .justify_center()
137                                .size(px(container_size))
138                                .child(
139                                    div()
140                                        .id("animate-in-from-top")
141                                        .absolute()
142                                        .size(px(element_size))
143                                        .left(px(left_offset))
144                                        .rounded_md()
145                                        .bg(gpui::blue())
146                                        .animate_in(AnimationDirection::FromTop, false),
147                                )
148                                .into_any_element(),
149                        ),
150                        single_example(
151                            "From Left",
152                            ContentGroup::new()
153                                .relative()
154                                .items_center()
155                                .justify_center()
156                                .size(px(container_size))
157                                .child(
158                                    div()
159                                        .id("animate-in-from-left")
160                                        .absolute()
161                                        .size(px(element_size))
162                                        .left(px(left_offset))
163                                        .rounded_md()
164                                        .bg(gpui::green())
165                                        .animate_in(AnimationDirection::FromLeft, false),
166                                )
167                                .into_any_element(),
168                        ),
169                        single_example(
170                            "From Right",
171                            ContentGroup::new()
172                                .relative()
173                                .items_center()
174                                .justify_center()
175                                .size(px(container_size))
176                                .child(
177                                    div()
178                                        .id("animate-in-from-right")
179                                        .absolute()
180                                        .size(px(element_size))
181                                        .left(px(left_offset))
182                                        .rounded_md()
183                                        .bg(gpui::yellow())
184                                        .animate_in(AnimationDirection::FromRight, false),
185                                )
186                                .into_any_element(),
187                        ),
188                    ],
189                )
190                .grow(),
191                example_group_with_title(
192                    "Fade and Animate In",
193                    vec![
194                        single_example(
195                            "From Bottom",
196                            ContentGroup::new()
197                                .relative()
198                                .items_center()
199                                .justify_center()
200                                .size(px(container_size))
201                                .child(
202                                    div()
203                                        .id("fade-animate-in-from-bottom")
204                                        .absolute()
205                                        .size(px(element_size))
206                                        .left(px(left_offset))
207                                        .rounded_md()
208                                        .bg(gpui::red())
209                                        .animate_in(AnimationDirection::FromBottom, true),
210                                )
211                                .into_any_element(),
212                        ),
213                        single_example(
214                            "From Top",
215                            ContentGroup::new()
216                                .relative()
217                                .items_center()
218                                .justify_center()
219                                .size(px(container_size))
220                                .child(
221                                    div()
222                                        .id("fade-animate-in-from-top")
223                                        .absolute()
224                                        .size(px(element_size))
225                                        .left(px(left_offset))
226                                        .rounded_md()
227                                        .bg(gpui::blue())
228                                        .animate_in(AnimationDirection::FromTop, true),
229                                )
230                                .into_any_element(),
231                        ),
232                        single_example(
233                            "From Left",
234                            ContentGroup::new()
235                                .relative()
236                                .items_center()
237                                .justify_center()
238                                .size(px(container_size))
239                                .child(
240                                    div()
241                                        .id("fade-animate-in-from-left")
242                                        .absolute()
243                                        .size(px(element_size))
244                                        .left(px(left_offset))
245                                        .rounded_md()
246                                        .bg(gpui::green())
247                                        .animate_in(AnimationDirection::FromLeft, true),
248                                )
249                                .into_any_element(),
250                        ),
251                        single_example(
252                            "From Right",
253                            ContentGroup::new()
254                                .relative()
255                                .items_center()
256                                .justify_center()
257                                .size(px(container_size))
258                                .child(
259                                    div()
260                                        .id("fade-animate-in-from-right")
261                                        .absolute()
262                                        .size(px(element_size))
263                                        .left(px(left_offset))
264                                        .rounded_md()
265                                        .bg(gpui::yellow())
266                                        .animate_in(AnimationDirection::FromRight, true),
267                                )
268                                .into_any_element(),
269                        ),
270                    ],
271                )
272                .grow(),
273            ])
274            .into_any_element()
275    }
276}