1use crate::{Easing, Frame, Spring, Target, Transition, value::sample_transition};
3#[cfg(feature = "gpui")]
4use std::time::Instant;
5use std::{error::Error, fmt, sync::Arc, time::Duration};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum AnimationError {
11 Keyframes,
13 Times,
15 ZeroPeriod,
17 Speed,
19 Label,
21 InfiniteSequence,
23 Index,
25}
26
27impl fmt::Display for AnimationError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(f, "invalid animation input: {self:?}")
30 }
31}
32
33impl Error for AnimationError {}
34
35#[derive(Clone, Copy, Debug, PartialEq)]
37pub struct Keyframe {
38 at: f64,
39 target: Option<Target>,
40 easing: Easing,
41}
42
43impl Keyframe {
44 pub fn new(at: f64, target: Target) -> Self {
46 Self {
47 at,
48 target: Some(target),
49 easing: Easing::default(),
50 }
51 }
52
53 pub fn current(at: f64) -> Self {
55 Self {
56 at,
57 target: None,
58 easing: Easing::default(),
59 }
60 }
61
62 pub fn easing(mut self, easing: Easing) -> Self {
64 self.easing = easing;
65
66 self
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub enum Repeat {
73 Count(u32),
75 Forever,
77}
78
79impl Default for Repeat {
80 fn default() -> Self {
81 Self::Count(0)
82 }
83}
84
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
87pub enum RepeatMode {
88 #[default]
90 Loop,
91 Reverse,
93 Mirror,
95}
96
97#[derive(Clone, Debug, PartialEq)]
98struct Clip {
99 frames: Vec<Keyframe>,
100 duration: Duration,
101 spring: Option<Spring>,
102}
103
104#[derive(Clone, Debug)]
106pub struct Animation {
107 clip: Arc<Clip>,
108 delay: Duration,
109 repeat: Repeat,
110 mode: RepeatMode,
111 repeat_delay: Duration,
112}
113
114impl Animation {
115 pub fn keyframes(
117 duration: Duration,
118 frames: impl IntoIterator<Item = Keyframe>,
119 ) -> Result<Self, AnimationError> {
120 let frames: Vec<_> = frames.into_iter().collect();
121
122 if frames.len() < 2 || frames.first().unwrap().at != 0.0 || frames.last().unwrap().at != 1.0
123 {
124 return Err(AnimationError::Keyframes);
125 }
126
127 if frames
128 .iter()
129 .any(|f| !f.at.is_finite() || !(0.0..=1.0).contains(&f.at))
130 || frames.windows(2).any(|p| p[0].at >= p[1].at)
131 {
132 return Err(AnimationError::Times);
133 }
134
135 Ok(Self {
136 clip: Arc::new(Clip {
137 frames,
138 duration,
139 spring: None,
140 }),
141 delay: Duration::ZERO,
142 repeat: Repeat::default(),
143 mode: RepeatMode::Loop,
144 repeat_delay: Duration::ZERO,
145 })
146 }
147
148 pub fn to(target: Target, duration: Duration, easing: Easing) -> Self {
150 Self::keyframes(
151 duration,
152 [
153 Keyframe::current(0.0),
154 Keyframe::new(1.0, target).easing(easing),
155 ],
156 )
157 .expect("valid two-frame clip")
158 }
159
160 pub fn spring(target: Target, spring: Spring, duration: Duration) -> Self {
164 let mut animation = Self::to(target, duration, Easing::default());
165
166 Arc::make_mut(&mut animation.clip).spring = Some(spring);
167
168 animation
169 }
170
171 pub fn delay(mut self, duration: Duration) -> Self {
173 self.delay = duration;
174
175 self
176 }
177
178 pub fn repeat(
180 mut self,
181 repeat: Repeat,
182 mode: RepeatMode,
183 delay: Duration,
184 ) -> Result<Self, AnimationError> {
185 if self.clip.duration.is_zero() && repeat != Repeat::Count(0) {
186 return Err(AnimationError::ZeroPeriod);
187 }
188
189 self.repeat = repeat;
190 self.mode = mode;
191 self.repeat_delay = delay;
192
193 Ok(self)
194 }
195
196 pub fn duration(&self) -> Option<Duration> {
198 match self.repeat {
199 Repeat::Forever => None,
200 Repeat::Count(count) => Some(
201 self.delay
202 .saturating_add(
203 self.clip
204 .duration
205 .saturating_mul(count)
206 .saturating_add(self.clip.duration),
207 )
208 .saturating_add(self.repeat_delay.saturating_mul(count)),
209 ),
210 }
211 }
212
213 fn same(&self, other: &Self) -> bool {
214 Arc::ptr_eq(&self.clip, &other.clip)
215 && self.delay == other.delay
216 && self.repeat == other.repeat
217 && self.mode == other.mode
218 && self.repeat_delay == other.repeat_delay
219 }
220
221 fn resolve(&self, time: Duration, origin: Frame, force_end: bool) -> (Frame, u64, bool) {
222 let duration = self.clip.duration.as_secs_f64();
223 let period = duration + self.repeat_delay.as_secs_f64();
224 let relative = time.saturating_sub(self.delay).as_secs_f64();
225 let done = force_end || self.duration().is_some_and(|end| time >= end);
226
227 let iteration = if done {
228 match self.repeat {
229 Repeat::Count(n) => u64::from(n),
230 Repeat::Forever => 0,
231 }
232 } else if period > 0.0 {
233 (relative / period).floor().min(u64::MAX as f64) as u64
234 } else {
235 0
236 };
237
238 let local = if done {
239 duration
240 } else {
241 (relative - iteration as f64 * period).clamp(0.0, duration)
242 };
243
244 let mut progress = if duration > 0.0 {
245 local / duration
246 } else {
247 1.0
248 };
249
250 if time < self.delay && !done {
251 progress = 0.0;
252 }
253
254 let odd = !iteration.is_multiple_of(2);
255 let reverse = odd && self.mode == RepeatMode::Reverse;
256
257 if reverse {
258 progress = 1.0 - progress;
259 }
260
261 let mut from = origin.target().inherit(
262 self.clip
263 .frames
264 .last()
265 .and_then(|f| f.target)
266 .unwrap_or_default(),
267 );
268
269 from = self.clip.frames[0].target.unwrap_or(from).inherit(from);
270 let first = from;
271
272 let final_target = self.clip.frames.iter().fold(from, |previous, frame| {
273 frame.target.unwrap_or(previous).inherit(previous)
274 });
275
276 if let Some(spring) = self.clip.spring {
277 let (start, end) = if odd && self.mode == RepeatMode::Mirror {
278 (final_target, first)
279 } else {
280 (first, final_target)
281 };
282
283 let velocity = if iteration == 0 {
284 origin_velocities(origin)
285 } else {
286 [0.0; 6]
287 };
288
289 let mut values = [0.0; 6];
290 let mut velocities = [0.0; 6];
291
292 for ix in 0..6 {
293 let (v, speed, _) = sample_transition(
294 start.values()[ix],
295 velocity[ix],
296 end.values()[ix],
297 Transition::Spring(spring),
298 progress * duration,
299 );
300
301 values[ix] = if progress == 0.0 {
302 start.values()[ix]
303 } else if progress == 1.0 {
304 end.values()[ix]
305 } else {
306 v
307 };
308 velocities[ix] = if progress == 1.0 || done || time < self.delay {
309 0.0
310 } else if reverse {
311 -speed
312 } else {
313 speed
314 };
315 }
316
317 return (
318 Frame::animated(
319 Target::from_values(values, start.mask() | end.mask() | 7),
320 velocities,
321 !done,
322 false,
323 ),
324 iteration,
325 done,
326 );
327 }
328
329 let mirrored = odd && self.mode == RepeatMode::Mirror;
330 let sample_progress = if mirrored { 1.0 - progress } else { progress };
331
332 let upper = self
333 .clip
334 .frames
335 .partition_point(|frame| frame.at < sample_progress)
336 .clamp(1, self.clip.frames.len() - 1);
337
338 let mut a = first;
339
340 for frame in self.clip.frames.iter().take(upper).skip(1) {
341 a = frame.target.unwrap_or(a).inherit(a);
342 }
343
344 let mut b = self.clip.frames[upper].target.unwrap_or(a).inherit(a);
345 let lo = self.clip.frames[upper - 1].at;
346 let hi = self.clip.frames[upper].at;
347 let mut u = (sample_progress - lo) / (hi - lo);
348
349 if mirrored {
350 std::mem::swap(&mut a, &mut b);
351 u = 1.0 - u;
352 }
353
354 let span = (hi - lo) * duration;
355 let easing = self.clip.frames[upper].easing;
356 let (weight, derivative) = easing.sample(u.clamp(0.0, 1.0));
357 let av = a.values();
358 let bv = b.values();
359
360 let values = std::array::from_fn(|ix| {
361 if u <= 0.0 {
362 av[ix]
363 } else if u >= 1.0 {
364 bv[ix]
365 } else {
366 crate::value::finite(av[ix] + (bv[ix] - av[ix]) * weight)
367 }
368 });
369
370 let velocity = std::array::from_fn(|ix| {
371 if done || time < self.delay || local >= duration || span == 0.0 {
372 0.0
373 } else {
374 crate::value::finite(
375 (bv[ix] - av[ix]) * derivative / span * if reverse { -1.0 } else { 1.0 },
376 )
377 }
378 });
379
380 (
381 Frame::animated(
382 Target::from_values(values, a.mask() | b.mask() | 7),
383 velocity,
384 !done,
385 false,
386 ),
387 iteration,
388 done,
389 )
390 }
391}
392
393fn origin_velocities(frame: Frame) -> [f64; 6] {
394 let basic = frame.velocity();
395 let layout = frame.layout_velocity();
396
397 [
398 basic[0],
399 basic[1],
400 basic[2],
401 layout[0].unwrap_or(0.0),
402 layout[1].unwrap_or(0.0),
403 layout[2].unwrap_or(0.0),
404 ]
405}
406
407#[derive(Clone, Debug)]
409pub struct Playback {
410 anchor: Duration,
411 offset: f64,
412 speed: f64,
413 paused: bool,
414 last: Duration,
415}
416
417impl Default for Playback {
418 fn default() -> Self {
419 Self {
420 anchor: Duration::ZERO,
421 offset: 0.0,
422 speed: 1.0,
423 paused: false,
424 last: Duration::ZERO,
425 }
426 }
427}
428
429impl Playback {
430 pub fn time(&mut self, now: Duration) -> Duration {
432 let now = now.max(self.last);
433
434 self.last = now;
435 let seconds = self.offset
436 + if self.paused {
437 0.0
438 } else {
439 now.saturating_sub(self.anchor).as_secs_f64() * self.speed
440 };
441
442 Duration::try_from_secs_f64(seconds.max(0.0)).unwrap_or(Duration::MAX)
443 }
444
445 pub fn pause(&mut self, now: Duration) {
447 self.offset = self.time(now).as_secs_f64();
448 self.anchor = now.max(self.last).max(self.anchor);
449 self.paused = true;
450 }
451
452 pub fn play(&mut self, now: Duration) {
454 if self.paused {
455 self.anchor = now.max(self.last).max(self.anchor);
456 self.paused = false;
457 }
458 }
459
460 pub fn seek(&mut self, time: Duration, now: Duration) {
462 self.last = self.last.max(now);
463 self.offset = time.as_secs_f64();
464 self.anchor = now.max(self.last).max(self.anchor);
465 }
466
467 pub fn set_speed(&mut self, speed: f64, now: Duration) -> Result<(), AnimationError> {
469 if !speed.is_finite() {
470 return Err(AnimationError::Speed);
471 }
472
473 self.offset = self.time(now).as_secs_f64();
474 self.anchor = now.max(self.last).max(self.anchor);
475 self.speed = speed;
476
477 Ok(())
478 }
479
480 pub fn is_playing(&self) -> bool {
482 !self.paused && self.speed != 0.0
483 }
484}
485
486#[derive(Debug)]
488pub struct AnimationState {
489 clip: Option<Animation>,
490 clock: Playback,
491 origin: Frame,
492 current: Frame,
493 complete: bool,
494 force_end: bool,
495 iteration: u64,
496 #[cfg(feature = "gpui")]
497 pub(crate) epoch: Instant,
498}
499
500impl Default for AnimationState {
501 fn default() -> Self {
502 Self::new(Target::default())
503 }
504}
505
506impl AnimationState {
507 pub fn new(initial: Target) -> Self {
509 Self {
510 clip: None,
511 clock: Playback::default(),
512 origin: Frame::at(initial),
513 current: Frame::at(initial),
514 complete: false,
515 force_end: false,
516 iteration: 0,
517 #[cfg(feature = "gpui")]
518 epoch: Instant::now(),
519 }
520 }
521
522 pub fn sample(&mut self, animation: &Animation, now: Duration, reduced_motion: bool) -> Frame {
525 if !self.clip.as_ref().is_some_and(|old| old.same(animation)) {
526 if let Some(old) = &self.clip {
527 let frame = old
528 .resolve(self.clock.time(now), self.origin, self.force_end)
529 .0;
530
531 let factor = if self.clock.is_playing() {
532 self.clock.speed
533 } else {
534 0.0
535 };
536
537 self.current = Frame::animated(
538 frame.target(),
539 origin_velocities(frame).map(|v| v * factor),
540 frame.is_animating(),
541 false,
542 );
543 }
544
545 self.origin = self.current;
546 let first = self.clip.is_none();
547
548 self.clip = Some(animation.clone());
549
550 if first {
551 self.clock.anchor = now.max(self.clock.last);
552 self.clock.last = self.clock.anchor;
553 } else {
554 self.clock.seek(Duration::ZERO, now);
555 }
556
557 self.complete = false;
558
559 if !first {
560 self.force_end = false;
561 }
562 }
563
564 self.force_end |= reduced_motion;
565 let time = self.clock.time(now);
566 let (frame, iteration, finished) = animation.resolve(time, self.origin, self.force_end);
567 let backward_end = self.clock.speed < 0.0 && time.is_zero();
568 let complete = finished || backward_end;
569
570 self.current = Frame::animated(
571 frame.target(),
572 origin_velocities(frame).map(|v| {
573 v * if self.clock.is_playing() {
574 self.clock.speed
575 } else {
576 0.0
577 }
578 }),
579 !complete && self.clock.is_playing(),
580 complete && !self.complete,
581 );
582 self.complete = complete;
583 self.iteration = iteration;
584
585 self.current
586 }
587
588 pub fn pause(&mut self, now: Duration) {
590 self.clock.pause(now);
591 }
592
593 pub fn play(&mut self, now: Duration) {
595 self.clock.play(now);
596 }
597
598 pub fn stop(&mut self, now: Duration) {
600 self.pause(now);
601 }
602
603 pub fn seek(&mut self, time: Duration, now: Duration) {
605 self.clock.seek(time, now);
606 }
607
608 pub fn set_speed(&mut self, speed: f64, now: Duration) -> Result<(), AnimationError> {
610 self.clock.set_speed(speed, now)
611 }
612
613 pub fn restart(&mut self, now: Duration) {
615 self.clock.seek(Duration::ZERO, now);
616 self.clock.play(now);
617 self.force_end = false;
618 self.complete = false;
619 }
620
621 pub fn finish(&mut self) {
623 self.force_end = true;
624 }
625
626 pub fn iteration(&self) -> u64 {
628 self.iteration
629 }
630
631 pub fn is_playing(&self) -> bool {
633 self.clock.is_playing()
634 }
635}
636
637#[derive(Clone, Copy, Debug, PartialEq, Eq)]
639pub enum StaggerFrom {
640 Start,
642 End,
644 Center,
646}
647
648pub fn stagger(
650 step: Duration,
651 ix: usize,
652 count: usize,
653 from: StaggerFrom,
654) -> Result<Duration, AnimationError> {
655 if count == 0 || ix >= count {
656 return Err(AnimationError::Index);
657 }
658
659 let distance = match from {
660 StaggerFrom::Start => ix as f64,
661 StaggerFrom::End => (count - 1 - ix) as f64,
662 StaggerFrom::Center => (ix as f64 - (count - 1) as f64 / 2.0).abs(),
663 };
664
665 Ok(Duration::try_from_secs_f64(step.as_secs_f64() * distance).unwrap_or(Duration::MAX))
666}
667
668#[derive(Debug)]
669struct Track {
670 at: Duration,
671 animation: Animation,
672 state: AnimationState,
673}
674
675#[derive(Debug, Default)]
677pub struct Timeline {
678 tracks: Vec<Track>,
679 frames: Vec<Frame>,
680 clock: Playback,
681 cursor: Option<Duration>,
682 labels: Vec<(String, Duration)>,
683 active: bool,
684}
685
686impl Timeline {
687 pub fn at(&mut self, at: Duration, animation: Animation) -> usize {
689 let end = animation
690 .duration()
691 .map(|duration| at.saturating_add(duration));
692
693 self.cursor = end;
694 let mut state = AnimationState::default();
695
696 state.sample(&animation, Duration::ZERO, false);
697 self.frames.push(Frame::at(Target::default()));
698 self.tracks.push(Track {
699 at,
700 animation,
701 state,
702 });
703
704 self.tracks.len() - 1
705 }
706
707 pub fn then(&mut self, animation: Animation) -> Result<usize, AnimationError> {
709 let at = if self.tracks.is_empty() {
710 Duration::ZERO
711 } else {
712 self.cursor.ok_or(AnimationError::InfiniteSequence)?
713 };
714
715 Ok(self.at(at, animation))
716 }
717
718 pub fn label(&mut self, name: impl Into<String>) -> Result<(), AnimationError> {
720 let name = name.into();
721
722 if self.labels.iter().any(|(n, _)| *n == name) {
723 return Err(AnimationError::Label);
724 }
725
726 let at = if self.tracks.is_empty() {
727 Duration::ZERO
728 } else {
729 self.cursor.ok_or(AnimationError::InfiniteSequence)?
730 };
731
732 self.labels.push((name, at));
733
734 Ok(())
735 }
736
737 pub fn at_label(
739 &mut self,
740 name: &str,
741 offset: Duration,
742 animation: Animation,
743 ) -> Result<usize, AnimationError> {
744 let at = self
745 .labels
746 .iter()
747 .find(|(n, _)| n == name)
748 .ok_or(AnimationError::Label)?
749 .1;
750
751 Ok(self.at(at.saturating_add(offset), animation))
752 }
753
754 pub fn restart(&mut self, now: Duration) {
756 self.clock.seek(Duration::ZERO, now);
757 self.clock.play(now);
758
759 for track in &mut self.tracks {
760 track.state.restart(now);
761 }
762 }
763
764 pub fn pause(&mut self, now: Duration) {
766 self.clock.pause(now);
767 }
768
769 pub fn play(&mut self, now: Duration) {
771 self.clock.play(now);
772 }
773
774 pub fn seek(&mut self, time: Duration, now: Duration) {
776 self.clock.seek(time, now);
777 }
778
779 pub fn set_speed(&mut self, speed: f64, now: Duration) -> Result<(), AnimationError> {
781 self.clock.set_speed(speed, now)
782 }
783
784 pub fn sample(&mut self, now: Duration, reduced_motion: bool) -> &[Frame] {
786 let playhead = self.clock.time(now);
787 let playing = self.clock.is_playing() && !(self.clock.speed < 0.0 && playhead.is_zero());
788
789 self.active = false;
790
791 for (track, output) in self.tracks.iter_mut().zip(&mut self.frames) {
792 let local = playhead.saturating_sub(track.at);
793
794 track.state.seek(local, now);
795 let frame = track.state.sample(&track.animation, now, reduced_motion);
796 let pending = playhead < track.at && !reduced_motion;
797
798 self.active |= (pending || frame.is_animating()) && playing;
799 *output = Frame::animated(
800 frame.target(),
801 origin_velocities(frame).map(|v| {
802 if pending || !playing {
803 0.0
804 } else {
805 v * self.clock.speed
806 }
807 }),
808 !pending && frame.is_animating() && playing,
809 !pending && frame.just_completed(),
810 );
811 }
812
813 &self.frames
814 }
815
816 pub fn is_animating(&self) -> bool {
818 self.active
819 }
820}
821
822impl PartialEq for Animation {
823 fn eq(&self, other: &Self) -> bool {
824 self.same(other)
825 }
826}