use std::time::Duration;
use gpui::{Pixels, Styled};
use super::easing::{
ease_in_bounce, ease_in_out, ease_out_bounce, ease_out_cubic, ease_out_elastic, ease_out_quint,
};
pub mod preset_duration {
use super::Duration;
pub const VERY_FAST: Duration = Duration::from_millis(100);
pub const FAST: Duration = Duration::from_millis(150);
pub const NORMAL: Duration = Duration::from_millis(200);
pub const SLOW: Duration = Duration::from_millis(300);
pub const VERY_SLOW: Duration = Duration::from_millis(400);
pub const INSTANT: Duration = Duration::ZERO;
}
pub mod defaults {
#[allow(dead_code)]
pub const SLIDE_DISTANCE_PX: f32 = 10.0;
#[allow(dead_code)]
pub const BOUNCE_DISTANCE_PX: f32 = 30.0;
pub const PULSE_MIN_OPACITY: f32 = 0.55;
pub const PULSE_MAX_OPACITY: f32 = 0.95;
}
#[derive(Debug, Clone)]
pub struct PresetAnimation {
pub duration: Duration,
pub easing_name: &'static str,
pub animation_type: AnimationType,
}
#[derive(Debug, Clone)]
pub enum AnimationType {
FadeIn,
FadeOut,
SlideIn(SlideDirection),
SlideOut(SlideDirection),
ScaleIn,
ScaleOut,
BounceIn,
BounceOut,
ElasticIn,
ElasticOut,
FadeSlideIn(SlideDirection),
FadeScaleIn,
}
#[derive(Debug, Clone, Copy)]
pub enum SlideDirection {
Left,
Right,
Up,
Down,
}
impl From<SlideDirection> for super::helpers::SlideDirection {
fn from(value: SlideDirection) -> Self {
match value {
SlideDirection::Left => Self::Left,
SlideDirection::Right => Self::Right,
SlideDirection::Up => Self::Up,
SlideDirection::Down => Self::Down,
}
}
}
pub struct FadeIn;
impl FadeIn {
pub fn new() -> Self {
Self
}
pub fn apply<E: Fn(f32) -> f32 + 'static>(
self,
duration: Duration,
easing: E,
) -> impl FnOnce(gpui::Div, f32) -> gpui::Div + 'static {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased_progress = easing(progress);
element.opacity(eased_progress)
}
}
pub fn apply_default(self, element: gpui::Div, progress: f32) -> gpui::Div {
element.opacity(progress)
}
}
impl Default for FadeIn {
fn default() -> Self {
Self::new()
}
}
pub struct FadeOut;
impl FadeOut {
pub fn new() -> Self {
Self
}
pub fn apply(self, element: gpui::Div, progress: f32) -> gpui::Div {
element.opacity(1.0 - progress)
}
}
impl Default for FadeOut {
fn default() -> Self {
Self::new()
}
}
pub fn fade_slide_in(duration: Duration) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased = ease_out_quint(progress);
element.opacity(eased).mt(gpui::px(10.0 - 6.0 * eased))
}
}
pub fn fade_slide_out(duration: Duration) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased = ease_out_quint(progress);
element.opacity(1.0 - eased).mt(gpui::px(4.0 + 6.0 * eased))
}
}
pub fn fade_slide_in_from(
direction: SlideDirection,
distance: Pixels,
) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_cubic(progress);
let translate = distance_f * (1.0 - eased);
match direction {
SlideDirection::Left => element.opacity(eased).ml(gpui::px(-translate)),
SlideDirection::Right => element.opacity(eased).ml(gpui::px(translate)),
SlideDirection::Up => element.opacity(eased).mt(gpui::px(-translate)),
SlideDirection::Down => element.opacity(eased).mt(gpui::px(translate)),
}
}
}
pub fn fade_slide_out_to(
direction: SlideDirection,
distance: Pixels,
) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_cubic(progress);
let translate = distance_f * eased;
let opacity = 1.0 - eased;
match direction {
SlideDirection::Left => element.opacity(opacity).ml(gpui::px(-translate)),
SlideDirection::Right => element.opacity(opacity).ml(gpui::px(translate)),
SlideDirection::Up => element.opacity(opacity).mt(gpui::px(-translate)),
SlideDirection::Down => element.opacity(opacity).mt(gpui::px(translate)),
}
}
}
pub fn pulse(duration: Duration) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased = ease_in_out(progress);
let opacity = defaults::PULSE_MIN_OPACITY
+ (defaults::PULSE_MAX_OPACITY - defaults::PULSE_MIN_OPACITY) * eased;
element.opacity(opacity)
}
}
pub fn fade_slide_in_left(distance: Pixels) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_cubic(progress);
let translate = -distance_f * (1.0 - eased);
element.opacity(eased).ml(gpui::px(translate))
}
}
pub fn fade_slide_in_right(distance: Pixels) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_cubic(progress);
let translate = distance_f * (1.0 - eased);
element.opacity(eased).ml(gpui::px(translate))
}
}
pub fn fade_slide_in_up(distance: Pixels) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_cubic(progress);
let translate = -distance_f * (1.0 - eased);
element.opacity(eased).mt(gpui::px(translate))
}
}
pub fn fade_slide_in_down(distance: Pixels) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_cubic(progress);
let translate = distance_f * (1.0 - eased);
element.opacity(eased).mt(gpui::px(translate))
}
}
pub struct ScaleIn;
impl ScaleIn {
pub fn new() -> Self {
Self
}
pub fn apply<E: Fn(f32) -> f32 + 'static>(
self,
duration: Duration,
easing: E,
) -> impl FnOnce(gpui::Div, f32) -> gpui::Div + 'static {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased_progress = easing(progress);
element.opacity(eased_progress)
}
}
pub fn apply_default(self, element: gpui::Div, progress: f32) -> gpui::Div {
let eased = ease_out_cubic(progress);
element.opacity(eased)
}
}
impl Default for ScaleIn {
fn default() -> Self {
Self::new()
}
}
pub struct ScaleOut;
impl ScaleOut {
pub fn new() -> Self {
Self
}
pub fn apply<E: Fn(f32) -> f32 + 'static>(
self,
duration: Duration,
easing: E,
) -> impl FnOnce(gpui::Div, f32) -> gpui::Div + 'static {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased_progress = easing(progress);
element.opacity(1.0 - eased_progress)
}
}
pub fn apply_default(self, element: gpui::Div, progress: f32) -> gpui::Div {
let eased = ease_out_cubic(progress);
element.opacity(1.0 - eased)
}
}
impl Default for ScaleOut {
fn default() -> Self {
Self::new()
}
}
pub fn fade_scale_in(duration: Duration) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased = ease_out_cubic(progress);
element.opacity(eased)
}
}
pub fn fade_scale_out(duration: Duration) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased = ease_out_cubic(progress);
element.opacity(1.0 - eased)
}
}
pub struct BounceIn;
impl BounceIn {
pub fn new() -> Self {
Self
}
pub fn apply<E: Fn(f32) -> f32 + 'static>(
self,
duration: Duration,
easing: E,
) -> impl FnOnce(gpui::Div, f32) -> gpui::Div + 'static {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased_progress = easing(progress);
let translate = -30.0 * (1.0 - eased_progress);
element.opacity(eased_progress).mt(gpui::px(translate))
}
}
pub fn apply_default(self, element: gpui::Div, progress: f32) -> gpui::Div {
let eased = ease_out_bounce(progress);
let translate = -30.0 * (1.0 - eased);
element.opacity(eased).mt(gpui::px(translate))
}
}
impl Default for BounceIn {
fn default() -> Self {
Self::new()
}
}
pub struct BounceOut;
impl BounceOut {
pub fn new() -> Self {
Self
}
pub fn apply<E: Fn(f32) -> f32 + 'static>(
self,
duration: Duration,
easing: E,
) -> impl FnOnce(gpui::Div, f32) -> gpui::Div + 'static {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased_progress = easing(progress);
let translate = 30.0 * eased_progress;
element
.opacity(1.0 - eased_progress)
.mt(gpui::px(translate))
}
}
pub fn apply_default(self, element: gpui::Div, progress: f32) -> gpui::Div {
let eased = ease_in_bounce(progress);
let translate = 30.0 * eased;
element.opacity(1.0 - eased).mt(gpui::px(translate))
}
}
impl Default for BounceOut {
fn default() -> Self {
Self::new()
}
}
pub fn bounce_in_left(distance: Pixels) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_bounce(progress);
let translate = -distance_f * (1.0 - eased);
element.opacity(eased).ml(gpui::px(translate))
}
}
pub fn bounce_in_right(distance: Pixels) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_bounce(progress);
let translate = distance_f * (1.0 - eased);
element.opacity(eased).ml(gpui::px(translate))
}
}
pub fn bounce_in_up(distance: Pixels) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_bounce(progress);
let translate = -distance_f * (1.0 - eased);
element.opacity(eased).mt(gpui::px(translate))
}
}
pub fn bounce_in_down(distance: Pixels) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_out_bounce(progress);
let translate = distance_f * (1.0 - eased);
element.opacity(eased).mt(gpui::px(translate))
}
}
pub fn bounce_out_to(
direction: SlideDirection,
distance: Pixels,
) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let distance_f: f32 = distance.into();
move |element: gpui::Div, progress: f32| {
let eased = ease_in_bounce(progress);
let translate = distance_f * eased;
let opacity = 1.0 - eased;
match direction {
SlideDirection::Left => element.opacity(opacity).ml(gpui::px(-translate)),
SlideDirection::Right => element.opacity(opacity).ml(gpui::px(translate)),
SlideDirection::Up => element.opacity(opacity).mt(gpui::px(-translate)),
SlideDirection::Down => element.opacity(opacity).mt(gpui::px(translate)),
}
}
}
pub struct ElasticIn;
impl ElasticIn {
pub fn new() -> Self {
Self
}
pub fn apply<E: Fn(f32) -> f32 + 'static>(
self,
duration: Duration,
easing: E,
) -> impl FnOnce(gpui::Div, f32) -> gpui::Div + 'static {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased_progress = easing(progress);
let overshoot = if eased_progress < 0.5 {
-10.0 * (1.0 - 2.0 * eased_progress)
} else {
0.0
};
element.opacity(eased_progress).mt(gpui::px(overshoot))
}
}
pub fn apply_default(self, element: gpui::Div, progress: f32) -> gpui::Div {
let eased = ease_out_elastic(progress);
element.opacity(eased)
}
}
impl Default for ElasticIn {
fn default() -> Self {
Self::new()
}
}
pub struct ElasticOut;
impl ElasticOut {
pub fn new() -> Self {
Self
}
pub fn apply<E: Fn(f32) -> f32 + 'static>(
self,
duration: Duration,
easing: E,
) -> impl FnOnce(gpui::Div, f32) -> gpui::Div + 'static {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased_progress = easing(progress);
let overshoot = if eased_progress > 0.5 {
10.0 * (2.0 * (eased_progress - 0.5))
} else {
0.0
};
element
.opacity(1.0 - eased_progress)
.mt(gpui::px(overshoot))
}
}
pub fn apply_default(self, element: gpui::Div, progress: f32) -> gpui::Div {
let eased = ease_out_elastic(progress);
element.opacity(1.0 - eased)
}
}
impl Default for ElasticOut {
fn default() -> Self {
Self::new()
}
}
pub fn elastic_scale_in(duration: Duration) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased = ease_out_elastic(progress);
element.opacity(eased)
}
}
pub fn elastic_scale_out(duration: Duration) -> impl Fn(gpui::Div, f32) -> gpui::Div {
let _ = duration;
move |element: gpui::Div, progress: f32| {
let eased = ease_out_elastic(progress);
element.opacity(1.0 - eased)
}
}