Files
Ely-GPUI-Components/src/motion/curve.rs
T
ZacharyZhang-NY a13343c2a5 T01 primitives: image, divider, backdrop, focus, pressable, measure, tooltip
Ely-built primitives plus gallery demos for every chapter-one entry,
including gpui natives. The gallery now scripts real input by posting
NSEvents to its own queue and shoots each interaction in both modes.
Codex gpt-6-astra (max): three rounds; the last finding, a two-line
doc comment, was fixed after the three-review cap.
2026-09-25 00:39:06 -04:00

82 lines
2.0 KiB
Rust

use std::time::Duration;
use gpui::{App, Pixels, px};
use crate::theme::ActiveTheme;
pub const FAST: Duration = Duration::from_millis(120);
pub const BASE: Duration = Duration::from_millis(200);
pub const SLOW: Duration = Duration::from_millis(320);
pub const THEME: Duration = Duration::from_millis(280);
/// Travel of small entrances.
pub const NUDGE: Pixels = px(4.0);
const DAMPING: f32 = 0.75;
const OMEGA: f32 = 6.91 / DAMPING;
/// `base`, or one millisecond under reduced motion.
pub fn duration(base: Duration, cx: &App) -> Duration {
if cx.theme().reduced_motion {
Duration::from_millis(1)
} else {
base
}
}
pub fn lerp(from: f32, to: f32, t: f32) -> f32 {
from + (to - from) * t
}
pub fn ease_out_cubic(t: f32) -> f32 {
1.0 - (1.0 - t).powi(3)
}
pub fn ease_in_out_cubic(t: f32) -> f32 {
if t < 0.5 {
4.0 * t * t * t
} else {
1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
}
}
/// Damped spring. Overshoots; not a gpui easing.
pub fn spring(t: f32) -> f32 {
if t >= 1.0 {
return 1.0;
}
let damped = OMEGA * (1.0 - DAMPING * DAMPING).sqrt();
let decay = (-DAMPING * OMEGA * t).exp();
1.0 - decay * ((damped * t).cos() + DAMPING * OMEGA / damped * (damped * t).sin())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn easings_pin_their_ends() {
for ease in [ease_out_cubic, ease_in_out_cubic, spring] {
assert!(ease(0.0).abs() < 1e-6);
assert_eq!(ease(1.0), 1.0);
}
}
#[test]
fn spring_overshoots_a_little_then_settles() {
let peak = (0..=100)
.map(|i| spring(i as f32 / 100.0))
.fold(0.0, f32::max);
assert!(peak > 1.01 && peak < 1.05, "peak {peak}");
assert!((spring(0.95) - 1.0).abs() < 0.01);
}
#[test]
fn ease_in_out_is_symmetric() {
for i in 0..=10 {
let t = i as f32 / 10.0;
assert!((ease_in_out_cubic(t) + ease_in_out_cubic(1.0 - t) - 1.0).abs() < 1e-5);
}
}
}