1#![deny(missing_docs)]
2
3pub mod easing;
6
7pub use easing::{Bezier, Easing, Linear, Spring, Step, StepPosition};
8
9use crate::{
10 ViewId,
11 style::{Style, StylePropRef},
12 unit::UnitExt,
13 view::StackOffset,
14};
15
16use std::any::Any;
17use std::rc::Rc;
18
19use crate::platform::{Duration, Instant};
20use floem_reactive::{RwSignal, SignalGet, Trigger, UpdaterEffect};
21use smallvec::{SmallVec, smallvec};
22
23#[derive(Clone, Debug)]
25pub struct KeyFrameProp {
26 val: Rc<dyn Any>,
28 id: u16,
30 easing: Rc<dyn Easing>,
32}
33
34#[derive(Clone, Debug)]
36pub enum KeyFrameStyle {
37 Computed,
39 Style(Style),
41}
42impl From<Style> for KeyFrameStyle {
43 fn from(value: Style) -> Self {
44 Self::Style(value)
45 }
46}
47
48#[derive(Clone, Debug)]
50pub struct KeyFrame {
51 #[allow(unused)]
52 id: u16,
54 style: KeyFrameStyle,
55 easing: Rc<dyn Easing>,
57}
58impl KeyFrame {
59 pub fn new(id: u16) -> Self {
61 Self {
62 id,
63 style: Style::default().into(),
64 easing: Rc::new(Spring::default()),
65 }
66 }
67
68 pub fn style(mut self, style: impl Fn(Style) -> Style) -> Self {
70 let style = style(Style::new());
71 match &mut self.style {
72 cs @ KeyFrameStyle::Computed => *cs = style.into(),
73 KeyFrameStyle::Style(s) => s.apply_mut(&style),
74 }
75 self
76 }
77
78 pub fn computed_style(mut self) -> Self {
80 self.style = KeyFrameStyle::Computed;
81 self
82 }
83
84 pub fn ease(mut self, easing: impl Easing + 'static) -> Self {
86 self.easing = Rc::new(easing);
87 self
88 }
89
90 pub fn ease_in_out(self) -> Self {
92 self.ease(Bezier::ease_in_out())
93 }
94
95 pub fn ease_spring(self) -> Self {
97 self.ease(Spring::default())
98 }
99
100 pub fn ease_linear(self) -> Self {
102 self.ease(Linear)
103 }
104
105 pub fn ease_in(self) -> Self {
107 self.ease(Bezier::ease_in())
108 }
109
110 pub fn ease_out(self) -> Self {
112 self.ease(Bezier::ease_out())
113 }
114}
115
116#[derive(Debug, Clone, Copy, Eq)]
118enum PropFrameKind {
119 Normal(u16),
120 Computed(u16),
121}
122impl PropFrameKind {
123 const fn inner(self) -> u16 {
124 match self {
125 Self::Normal(val) => val,
126 Self::Computed(val) => val,
127 }
128 }
129}
130impl PartialOrd for PropFrameKind {
131 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
132 Some(self.cmp(other))
133 }
134}
135impl Ord for PropFrameKind {
136 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
137 self.inner().cmp(&other.inner())
138 }
139}
140impl PartialEq for PropFrameKind {
141 fn eq(&self, other: &Self) -> bool {
142 self.inner() == other.inner()
143 }
144}
145
146#[derive(Debug, Clone, Copy)]
148struct PropFrames {
149 lower_idx: Option<PropFrameKind>,
151 upper_idx: Option<PropFrameKind>,
153}
154
155#[derive(Debug, Clone, Default)]
158pub(crate) struct PropCache {
159 prop_map: imbl::HashMap<StylePropRef, SmallVec<[PropFrameKind; 5]>>,
161 computed_idxs: SmallVec<[u16; 2]>,
163}
164impl PropCache {
165 fn get_prop_frames(&self, prop: StylePropRef, target_idx: u16) -> Option<PropFrames> {
169 self.prop_map.get(&prop).map(|frames| {
170 match frames.binary_search(&PropFrameKind::Normal(target_idx)) {
171 Ok(exact_idx) => {
172 let lower = Some(frames[exact_idx]);
174 let upper = frames.get(exact_idx + 1).copied();
175 PropFrames {
176 lower_idx: lower,
177 upper_idx: upper,
178 }
179 }
180 Err(pos) => {
181 let lower = if pos > 0 {
183 Some(frames[pos - 1]) } else {
185 None
186 };
187 let upper = frames.get(pos).copied(); PropFrames {
189 lower_idx: lower,
190 upper_idx: upper,
191 }
192 }
193 }
194 })
195 }
196
197 fn insert_prop(&mut self, prop: StylePropRef, idx: PropFrameKind) {
198 match self.prop_map.entry(prop) {
199 imbl::hashmap::Entry::Occupied(mut oe) => {
200 if let Err(pos) = oe.get().binary_search(&idx) {
201 oe.get_mut().insert(pos, idx)
202 }
203 }
204 imbl::hashmap::Entry::Vacant(ve) => {
205 ve.insert(smallvec![idx]);
206 }
207 }
208 }
209
210 fn insert_computed_prop(&mut self, prop: StylePropRef, idx: PropFrameKind) {
211 if let imbl::hashmap::Entry::Occupied(mut oe) = self.prop_map.entry(prop) {
214 if let Err(pos) = oe.get().binary_search(&idx) {
215 oe.get_mut().insert(pos, idx)
216 } else {
217 unreachable!(
218 "this should err because a computed prop shouldn't be inserted more than once. "
219 )
220 }
221 }
222 }
223
224 fn remove_prop(&mut self, prop: StylePropRef, idx: u16) {
225 if let imbl::hashmap::Entry::Occupied(mut oe) = self.prop_map.entry(prop)
226 && let Ok(pos) = oe.get().binary_search(&PropFrameKind::Normal(idx))
227 {
228 oe.get_mut().remove(pos);
229 }
230 }
231
232 fn insert_computed(&mut self, idx: u16) {
234 if let Err(pos) = self.computed_idxs.binary_search(&idx) {
235 self.computed_idxs.insert(pos, idx)
236 }
237 }
238
239 fn remove_computed(&mut self, idx: u16) {
241 if let Ok(pos) = self.computed_idxs.binary_search(&idx) {
242 self.computed_idxs.remove(pos);
243 }
244 }
245}
246
247#[derive(Debug, Clone, Copy)]
250pub enum ReverseOnce {
251 Never,
253 Val(bool),
256}
257impl ReverseOnce {
258 pub fn set(&mut self, val: bool) {
260 if let Self::Val(v) = self {
261 *v = val;
262 }
263 }
264
265 pub const fn is_rev(self) -> bool {
267 match self {
268 Self::Never => false,
269 Self::Val(v) => v,
270 }
271 }
272}
273
274#[derive(Clone, Debug)]
276pub enum RepeatMode {
277 LoopForever,
281 Times(usize),
286}
287
288#[derive(Debug, Clone)]
289pub(crate) enum AnimState {
290 Idle,
291 Stopped,
292 Paused {
293 elapsed: Option<Duration>,
294 },
295 PassInProgress {
299 started_on: Instant,
300 elapsed: Duration,
301 },
302 ExtMode {
303 started_on: Instant,
304 elapsed: Duration,
305 },
306 PassFinished {
309 elapsed: Duration,
310 was_in_ext: bool,
311 },
312 Completed {
314 elapsed: Option<Duration>,
315 was_reversing: bool,
316 },
317}
318
319#[derive(Debug, PartialEq, Eq, Clone, Copy)]
320pub enum AnimStateKind {
322 Idle,
324 Paused,
326 Stopped,
328 PassInProgress,
332 PassFinished,
334 Completed,
336}
337
338#[derive(Debug, PartialEq, Eq, Clone, Copy)]
339pub enum AnimStateCommand {
341 Pause,
343 Resume,
345 Start,
347 Stop,
349 Reverse,
351}
352
353type EffectStateVec = SmallVec<[RwSignal<SmallVec<[(ViewId, StackOffset<Animation>); 1]>>; 1]>;
354
355#[derive(Debug, Clone)]
359pub struct Animation {
360 pub(crate) state: AnimState,
361 pub(crate) effect_states: EffectStateVec,
362 pub(crate) auto_reverse: bool,
363 pub(crate) delay: Duration,
364 pub(crate) delay_on_reverse: bool,
365 pub(crate) duration: Duration,
366 pub(crate) repeat_mode: RepeatMode,
367 pub(crate) repeat_count: usize,
369 pub(crate) run_on_remove: bool,
371 pub(crate) run_on_create: bool,
372 pub(crate) reverse_once: ReverseOnce,
373 pub(crate) max_key_frame_num: u16,
374 pub(crate) apply_when_finished: bool,
375 pub(crate) folded_style: Style,
376 pub(crate) key_frames: imbl::HashMap<u16, KeyFrame>,
377 pub(crate) props_in_ext_progress: imbl::HashMap<StylePropRef, (KeyFrameProp, KeyFrameProp)>,
379 pub(crate) cache: PropCache,
380 pub(crate) on_start: Trigger,
382 pub(crate) on_visual_complete: Trigger,
386 pub(crate) on_complete: Trigger,
388 pub(crate) debug_description: Option<String>,
389}
390impl Default for Animation {
391 fn default() -> Self {
392 Self {
393 state: AnimState::Idle,
394 effect_states: SmallVec::new(),
395 auto_reverse: false,
396 delay: Duration::ZERO,
397 delay_on_reverse: false,
398 duration: Duration::from_millis(200),
399 repeat_mode: RepeatMode::Times(1),
400 repeat_count: 0,
401 run_on_remove: false,
402 run_on_create: false,
403 reverse_once: ReverseOnce::Val(false),
404 max_key_frame_num: 100,
405 apply_when_finished: false,
406 folded_style: Style::new(),
407 cache: Default::default(),
408 key_frames: imbl::HashMap::new(),
409 props_in_ext_progress: imbl::HashMap::new(),
410 on_start: Trigger::new(),
411 on_complete: Trigger::new(),
412 on_visual_complete: Trigger::new(),
413 debug_description: None,
414 }
415 }
416}
417
418impl Animation {
420 pub fn new() -> Self {
422 Self::default()
423 }
424
425 pub fn view_transition(self) -> Self {
428 self.run_on_create(true)
429 .run_on_remove(true)
430 .initial_state(AnimStateCommand::Stop)
431 .keyframe(0, |f| f.computed_style().ease(Spring::gentle()))
432 .keyframe(100, |f| f.computed_style().ease(Spring::gentle()))
433 }
434
435 pub fn view_transition_with_ease(self, ease: impl Easing + 'static + Clone) -> Self {
437 self.view_transition()
438 .keyframe(0, |f| f.computed_style().ease(ease.clone()))
439 .keyframe(100, |f| f.computed_style().ease(ease.clone()))
440 }
441
442 pub fn scale_effect(self) -> Self {
444 self.view_transition()
445 .keyframe(0, |f| f.style(|s| s.scale(0.pct())))
446 .debug_name("Scale the width and height from zero to the default")
447 }
448
449 pub fn scale_size_effect(self) -> Self {
451 self.view_transition()
452 .keyframe(0, |f| f.style(|s| s.size(0, 0)))
453 .debug_name("Scale the width and height from zero to the default")
454 }
455}
456
457impl Animation {
459 pub fn keyframe(mut self, frame_id: u16, key_frame: impl Fn(KeyFrame) -> KeyFrame) -> Self {
464 let frame = key_frame(KeyFrame::new(frame_id));
465 if let KeyFrameStyle::Style(ref style) = frame.style {
466 self.cache.remove_computed(frame_id);
468 for prop in style.style_props() {
469 self.cache
471 .insert_prop(prop, PropFrameKind::Normal(frame_id));
472 }
473 } else {
474 self.cache.insert_computed(frame_id);
475 }
476
477 match self.key_frames.entry(frame_id) {
479 imbl::hashmap::Entry::Occupied(mut oe) => {
480 let e_frame = oe.get_mut();
481 match (&mut e_frame.style, frame.style) {
482 (KeyFrameStyle::Computed, KeyFrameStyle::Computed) => {}
483 (s @ KeyFrameStyle::Computed, KeyFrameStyle::Style(ns)) => {
484 *s = KeyFrameStyle::Style(ns);
485 }
486 (s @ KeyFrameStyle::Style(_), KeyFrameStyle::Computed) => {
487 *s = KeyFrameStyle::Computed;
488 }
489 (KeyFrameStyle::Style(s), KeyFrameStyle::Style(ns)) => {
490 s.apply_mut(&ns);
491 }
492 }
493 e_frame.easing = frame.easing;
494 }
495 imbl::hashmap::Entry::Vacant(ve) => {
496 ve.insert(frame);
497 }
498 }
499 self
500 }
501
502 pub fn keyframe_override(
507 mut self,
508 frame_id: u16,
509 key_frame: impl Fn(KeyFrame) -> KeyFrame,
510 ) -> Self {
511 let frame = key_frame(KeyFrame::new(frame_id));
512 let frame_style = frame.style.clone();
513 if let Some(f) = self.key_frames.insert(frame_id, frame)
514 && let KeyFrameStyle::Style(style) = f.style
515 {
516 for prop in style.style_props() {
517 self.cache.remove_prop(prop, frame_id);
518 }
519 }
520 if let KeyFrameStyle::Style(style) = frame_style {
521 self.cache.insert_computed(frame_id);
522 for prop in style.style_props() {
523 self.cache
524 .insert_prop(prop, PropFrameKind::Normal(frame_id));
525 }
526 } else {
527 self.cache.remove_computed(frame_id);
528 }
529 self
530 }
531
532 pub const fn duration(mut self, duration: Duration) -> Self {
537 self.duration = duration;
538 self
539 }
540
541 pub fn with_duration(self, duration: impl FnOnce(Self, Duration) -> Self) -> Self {
543 let d = self.duration;
544 duration(self, d)
545 }
546
547 pub fn apply_if(self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self {
549 if cond { f(self) } else { self }
550 }
551
552 pub fn on_create(self, on_create: impl FnOnce(Trigger) + 'static) -> Self {
554 on_create(self.on_start);
555 self
556 }
557
558 pub fn on_visual_complete(self, on_visual_complete: impl FnOnce(Trigger) + 'static) -> Self {
560 on_visual_complete(self.on_visual_complete);
561 self
562 }
563
564 pub fn on_complete(self, on_complete: impl FnOnce(Trigger) + 'static) -> Self {
566 on_complete(self.on_complete);
567 self
568 }
569
570 pub const fn run_on_create(mut self, run_on_create: bool) -> Self {
574 self.run_on_create = run_on_create;
575 self
576 }
577
578 pub const fn only_on_create(mut self) -> Self {
580 self.run_on_remove = false;
581 self.run_on_create = true;
582 self
583 }
584
585 pub const fn run_on_remove(mut self, run_on_remove: bool) -> Self {
588 self.run_on_remove = run_on_remove;
589 self
590 }
591
592 pub const fn only_on_remove(mut self) -> Self {
594 self.run_on_remove = true;
595 self.run_on_create = false;
596 self
597 }
598
599 pub const fn apply_when_finished(mut self, apply: bool) -> Self {
601 self.apply_when_finished = apply;
602 self
603 }
604
605 pub const fn auto_reverse(mut self, auto_rev: bool) -> Self {
608 self.auto_reverse = auto_rev;
609 self
610 }
611
612 pub const fn reverse_on_exit(mut self, allow: bool) -> Self {
614 if allow {
615 self.reverse_once = ReverseOnce::Val(false);
616 } else {
617 self.reverse_once = ReverseOnce::Never;
618 }
619 self
620 }
621
622 pub const fn delay(mut self, delay: Duration) -> Self {
624 self.delay = delay;
625 self
626 }
627
628 pub const fn delay_on_reverse(mut self, on_reverse: bool) -> Self {
630 self.delay_on_reverse = on_reverse;
631 self
632 }
633
634 pub const fn repeat(mut self, repeat: bool) -> Self {
636 self.repeat_mode = if repeat {
637 RepeatMode::LoopForever
638 } else {
639 RepeatMode::Times(1)
640 };
641 self
642 }
643
644 pub const fn repeat_times(mut self, times: usize) -> Self {
646 self.repeat_mode = RepeatMode::Times(times);
647 self
648 }
649
650 pub const fn max_key_frame(mut self, max: u16) -> Self {
658 self.max_key_frame_num = max;
659 self
660 }
661
662 pub fn initial_state(mut self, command: AnimStateCommand) -> Self {
664 self.transition(command);
665 self
666 }
667
668 pub fn state(
671 mut self,
672 command: impl Fn() -> AnimStateCommand + 'static,
673 apply_initial: bool,
674 ) -> Self {
675 let states = RwSignal::new(SmallVec::new());
676 self.effect_states.push(states);
677 let initial_command = UpdaterEffect::new(command, move |command| {
678 for (view_id, stack_offset) in states.get_untracked() {
679 view_id.update_animation_state(stack_offset, command)
680 }
681 });
682 if apply_initial {
683 self.transition(initial_command);
684 }
685 self
686 }
687
688 pub fn pause(self, trigger: impl Fn() + 'static) -> Self {
690 self.state(
691 move || {
692 trigger();
693 AnimStateCommand::Pause
694 },
695 false,
696 )
697 }
698
699 pub fn resume(self, trigger: impl Fn() + 'static) -> Self {
701 self.state(
702 move || {
703 trigger();
704 AnimStateCommand::Resume
705 },
706 false,
707 )
708 }
709
710 pub fn start(self, trigger: impl Fn() + 'static) -> Self {
712 self.state(
713 move || {
714 trigger();
715 AnimStateCommand::Start
716 },
717 false,
718 )
719 }
720
721 pub fn reverse(self, trigger: impl Fn() + 'static) -> Self {
725 self.state(
726 move || {
727 trigger();
728 AnimStateCommand::Reverse
729 },
730 false,
731 )
732 }
733
734 pub fn stop(self, trigger: impl Fn() + 'static) -> Self {
736 self.state(
737 move || {
738 trigger();
739 AnimStateCommand::Stop
740 },
741 false,
742 )
743 }
744
745 pub fn debug_name(mut self, description: impl Into<String>) -> Self {
747 match &mut self.debug_description {
748 Some(inner_desc) => {
749 inner_desc.push_str("; ");
750 inner_desc.push_str(&description.into())
751 }
752 val @ None => *val = Some(description.into()),
753 }
754 self
755 }
756
757 #[allow(unused)]
758 pub(crate) fn pause_mut(mut self) {
759 self.transition(AnimStateCommand::Pause)
760 }
761
762 #[allow(unused)]
763 pub(crate) fn resume_mut(mut self) {
764 self.transition(AnimStateCommand::Resume)
765 }
766
767 pub(crate) fn start_mut(&mut self) {
768 self.transition(AnimStateCommand::Start)
769 }
770
771 pub(crate) fn reverse_mut(&mut self) {
772 self.transition(AnimStateCommand::Reverse)
773 }
774
775 #[allow(unused)]
776 pub(crate) fn stop_mut(&mut self) {
777 self.transition(AnimStateCommand::Stop)
778 }
779
780 pub const fn state_kind(&self) -> AnimStateKind {
782 match self.state {
783 AnimState::Idle => AnimStateKind::Idle,
784 AnimState::Stopped => AnimStateKind::Stopped,
785 AnimState::PassInProgress { .. } => AnimStateKind::PassInProgress,
786 AnimState::ExtMode { .. } => AnimStateKind::PassInProgress,
787 AnimState::PassFinished { .. } => AnimStateKind::PassFinished,
788 AnimState::Completed { .. } => AnimStateKind::Completed,
789 AnimState::Paused { .. } => AnimStateKind::Paused,
790 }
791 }
792
793 pub fn elapsed(&self) -> Option<Duration> {
795 match &self.state {
796 AnimState::Idle => None,
797 AnimState::Stopped => None,
798 AnimState::PassInProgress {
799 started_on,
800 elapsed,
801 }
802 | AnimState::ExtMode {
803 started_on,
804 elapsed,
805 } => {
806 let duration = Instant::now() - *started_on;
807 Some(*elapsed + duration)
808 }
809 AnimState::PassFinished { elapsed, .. } => Some(*elapsed),
810 AnimState::Completed { elapsed, .. } => *elapsed,
811 AnimState::Paused { elapsed } => *elapsed,
812 }
813 }
814
815 pub fn advance(&mut self) {
817 let use_delay = self.use_delay();
818 match &mut self.state {
819 AnimState::Idle => {
820 self.start_mut();
821 self.on_start.notify();
822 }
823 AnimState::PassInProgress {
824 started_on,
825 elapsed,
826 } => {
827 let now = Instant::now();
828 let duration = now - *started_on;
829 let og_elapsed = *elapsed;
830 *elapsed = duration;
831
832 let temp_elapsed = if *elapsed <= self.delay && use_delay {
833 Duration::ZERO
835 } else if use_delay {
836 *elapsed - self.delay
837 } else {
838 *elapsed
839 };
840
841 if temp_elapsed >= self.duration {
842 if self.props_in_ext_progress.is_empty() {
843 self.state = AnimState::PassFinished {
844 elapsed: *elapsed,
845 was_in_ext: false,
846 };
847 } else {
848 self.on_visual_complete.notify();
849 self.state = AnimState::ExtMode {
850 started_on: *started_on,
851 elapsed: og_elapsed,
852 };
853 }
854 }
855 }
856 AnimState::ExtMode {
857 started_on,
858 elapsed,
859 } => {
860 let now = Instant::now();
861 let duration = now - *started_on;
862 *elapsed = duration;
863
864 if self.props_in_ext_progress.is_empty() {
865 self.state = AnimState::PassFinished {
866 elapsed: *elapsed,
867 was_in_ext: true,
868 };
869 }
870 }
871 AnimState::PassFinished {
872 elapsed,
873 was_in_ext,
874 } => match self.repeat_mode {
875 RepeatMode::LoopForever => {
876 if self.reverse_once.is_rev() {
877 self.reverse_once.set(false);
878 } else if self.auto_reverse {
879 self.reverse_once.set(true);
880 }
881 self.state = AnimState::PassInProgress {
882 started_on: Instant::now(),
883 elapsed: Duration::ZERO,
884 }
885 }
886 RepeatMode::Times(times) => {
887 self.repeat_count += 1;
888 if self.repeat_count >= times {
889 let was_reversing = self.reverse_once.is_rev();
890 self.reverse_once.set(false);
891 self.on_complete.notify();
892 if !*was_in_ext {
893 self.on_visual_complete.notify();
894 }
895 self.state = AnimState::Completed {
896 elapsed: Some(*elapsed),
897 was_reversing,
898 }
899 } else {
900 self.state = AnimState::PassInProgress {
901 started_on: Instant::now(),
902 elapsed: Duration::ZERO,
903 }
904 }
905 }
906 },
907 AnimState::Paused { .. } => {
908 debug_assert!(false, "Tried to advance a paused animation")
909 }
910 AnimState::Stopped => {
911 debug_assert!(false, "Tried to advance a stopped animation")
912 }
913 AnimState::Completed { was_reversing, .. } => {
914 if self.auto_reverse && !*was_reversing {
915 self.reverse_mut();
916 } else {
917 self.state = AnimState::Stopped;
918 }
919 }
920 }
921 }
922
923 pub(crate) fn transition(&mut self, command: AnimStateCommand) {
924 match command {
925 AnimStateCommand::Pause => {
926 self.state = AnimState::Paused {
927 elapsed: self.elapsed(),
928 }
929 }
930 AnimStateCommand::Resume => {
931 if let AnimState::Paused { elapsed } = &self.state {
932 self.state = AnimState::PassInProgress {
933 started_on: Instant::now(),
934 elapsed: elapsed.unwrap_or(Duration::ZERO),
935 }
936 }
937 }
938 AnimStateCommand::Start => {
939 self.reverse_once.set(false);
940 Rc::make_mut(&mut self.folded_style.map).clear();
941 self.repeat_count = 0;
942 self.state = AnimState::PassInProgress {
943 started_on: Instant::now(),
944 elapsed: Duration::ZERO,
945 }
946 }
947 AnimStateCommand::Reverse => {
948 self.reverse_once.set(true);
949 Rc::make_mut(&mut self.folded_style.map).clear();
950 self.repeat_count = 0;
951 self.state = AnimState::PassInProgress {
952 started_on: Instant::now(),
953 elapsed: Duration::ZERO,
954 }
955 }
956 AnimStateCommand::Stop => {
957 self.repeat_count = 0;
958 self.state = AnimState::Stopped;
959 }
960 }
961 }
962
963 pub(crate) fn total_time_percent(&self) -> f64 {
965 if self.duration == Duration::ZERO {
966 return 0.;
967 }
968 let mut elapsed = self.elapsed().unwrap_or(Duration::ZERO);
969 if self.use_delay() {
970 elapsed = elapsed.saturating_sub(self.delay);
971 }
972 let percent = elapsed.as_secs_f64() / self.duration.as_secs_f64();
973
974 if self.reverse_once.is_rev() {
975 1. - percent
976 } else {
977 percent
978 }
979 }
980
981 fn is_reversing(&self) -> bool {
982 self.reverse_once.is_rev()
983 }
984
985 fn use_delay(&self) -> bool {
986 !self.is_reversing() || self.delay_on_reverse
988 }
989
990 pub(crate) fn get_current_kf_props(
992 &self,
993 prop: StylePropRef,
994 frame_target: u16,
995 computed_style: &Style,
996 ) -> Option<(KeyFrameProp, KeyFrameProp)> {
997 let PropFrames {
998 lower_idx,
999 upper_idx,
1000 } = self.cache.get_prop_frames(prop, frame_target)?;
1001
1002 let mut upper_computed = false;
1003
1004 let upper = {
1005 let upper = upper_idx?;
1006 let frame = self
1007 .key_frames
1008 .get(&upper.inner())
1009 .expect("If the value is in the cache, it should also be in the key frames");
1010
1011 let prop = match &frame.style {
1012 KeyFrameStyle::Computed => {
1013 debug_assert!(
1014 matches!(upper, PropFrameKind::Computed(_)),
1015 "computed frame should have come from matching computed idx"
1016 );
1017 upper_computed = true;
1018 computed_style
1019 .map
1020 .get(&prop.key)
1021 .expect("was in the cache as a computed frame")
1022 .clone()
1023 }
1024 KeyFrameStyle::Style(s) => s.map.get(&prop.key).expect("same as above").clone(),
1025 };
1026
1027 KeyFrameProp {
1028 id: upper.inner(),
1029 val: prop,
1030 easing: frame.easing.clone(),
1031 }
1032 };
1033
1034 let lower = {
1035 let lower = lower_idx?;
1036 let frame = self
1037 .key_frames
1038 .get(&lower.inner())
1039 .expect("If the value is in the cache, it should also be in the key frames");
1040
1041 let prop = match &frame.style {
1042 KeyFrameStyle::Computed => {
1043 debug_assert!(
1044 matches!(lower, PropFrameKind::Computed(_)),
1045 "computed frame should have come from matching computed idx"
1046 );
1047 if upper_computed {
1048 return None;
1050 }
1051 computed_style
1052 .map
1053 .get(&prop.key)
1054 .expect("was in the cache as a computed frame")
1055 .clone()
1056 }
1057 KeyFrameStyle::Style(s) => s.map.get(&prop.key).expect("same as above").clone(),
1058 };
1059
1060 KeyFrameProp {
1061 id: lower.inner(),
1062 val: prop,
1063 easing: frame.easing.clone(),
1064 }
1065 };
1066
1067 if self.is_reversing() {
1068 Some((upper, lower))
1069 } else {
1070 Some((lower, upper))
1071 }
1072 }
1073
1074 pub fn animate_into(&mut self, computed_style: &mut Style) {
1076 let computed_idxs = self.cache.computed_idxs.clone();
1080 for computed_idx in &computed_idxs {
1081 for prop in computed_style.style_props() {
1083 self.cache
1084 .insert_computed_prop(prop, PropFrameKind::Computed(*computed_idx));
1085 }
1086 }
1087 let local_percents: Vec<_> = self
1088 .props_in_ext_progress
1089 .iter()
1090 .map(|(p, (l, u))| (*p, self.get_local_percent(l.id, u.id)))
1091 .collect();
1092
1093 self.props_in_ext_progress.retain(|p, (_l, u)| {
1094 let local_percent = local_percents
1095 .iter()
1096 .find(|&&(prop, _)| prop == *p)
1097 .map(|&(_, percent)| percent)
1098 .unwrap_or_default();
1099 !u.easing.finished(local_percent)
1100 });
1101 for (ext_prop, (l, u)) in &self.props_in_ext_progress {
1102 let local_percent = local_percents
1103 .iter()
1104 .find(|&&(prop, _)| prop == *ext_prop)
1105 .map(|&(_, percent)| percent)
1106 .unwrap_or_default();
1107
1108 let eased_time = u.easing.eval(local_percent);
1109 if let Some(interpolated) =
1110 (ext_prop.info().interpolate)(&*l.val.clone(), &*u.val.clone(), eased_time)
1111 {
1112 Rc::make_mut(&mut self.folded_style.map).insert(ext_prop.key, interpolated);
1113 }
1114 }
1115
1116 let percent = self.total_time_percent();
1117 let frame_target = (self.max_key_frame_num as f64 * percent).round() as u16;
1118
1119 let props = self.cache.prop_map.keys();
1120
1121 for prop in props {
1122 if self.props_in_ext_progress.contains_key(prop) {
1123 continue;
1124 }
1125 let Some((prev, target)) =
1126 self.get_current_kf_props(*prop, frame_target, computed_style)
1127 else {
1128 continue;
1129 };
1130 let local_percent = self.get_local_percent(prev.id, target.id);
1131 let easing = target.easing.clone();
1132 if (local_percent > 0.97) && !easing.finished(local_percent) {
1135 self.props_in_ext_progress
1136 .insert(*prop, (prev.clone(), target.clone()));
1137 } else {
1138 self.props_in_ext_progress.remove(prop);
1139 }
1140 let eased_time = easing.eval(local_percent);
1141 if let Some(interpolated) =
1142 (prop.info().interpolate)(&*prev.val.clone(), &*target.val.clone(), eased_time)
1143 {
1144 Rc::make_mut(&mut self.folded_style.map).insert(prop.key, interpolated);
1145 }
1146 }
1147
1148 computed_style.apply_mut(&self.folded_style);
1149
1150 for computed_idx in computed_idxs {
1152 for prop in computed_style.style_props() {
1153 self.cache.remove_prop(prop, computed_idx);
1154 }
1155 }
1156 }
1157
1158 pub(crate) fn get_local_percent(&self, prev_frame: u16, target_frame: u16) -> f64 {
1160 let (low_frame, high_frame) = if self.is_reversing() {
1162 (target_frame as f64, prev_frame as f64)
1163 } else {
1164 (prev_frame as f64, target_frame as f64)
1165 };
1166 let total_num_frames = self.max_key_frame_num as f64;
1167 let low_frame_percent = low_frame / total_num_frames;
1168 let high_frame_percent = high_frame / total_num_frames;
1169 let keyframe_range = (high_frame_percent.max(0.001) - low_frame_percent.max(0.001)).abs();
1170 let total_time_percent = self.total_time_percent();
1171 let local = (total_time_percent - low_frame_percent) / keyframe_range;
1172
1173 if self.is_reversing() {
1174 1. - local
1175 } else {
1176 local
1177 }
1178 }
1179
1180 pub fn is_idle(&self) -> bool {
1182 self.state_kind() == AnimStateKind::Idle
1183 }
1184
1185 pub fn is_in_progress(&self) -> bool {
1187 self.state_kind() == AnimStateKind::PassInProgress
1188 }
1189
1190 pub fn is_completed(&self) -> bool {
1192 self.state_kind() == AnimStateKind::Completed
1193 }
1194
1195 pub fn is_stopped(&self) -> bool {
1197 self.state_kind() == AnimStateKind::Stopped
1198 }
1199
1200 pub const fn can_advance(&self) -> bool {
1202 match self.state_kind() {
1203 AnimStateKind::PassFinished
1204 | AnimStateKind::PassInProgress
1205 | AnimStateKind::Idle
1206 | AnimStateKind::Completed => true,
1207 AnimStateKind::Paused | AnimStateKind::Stopped => false,
1208 }
1209 }
1210
1211 pub const fn is_auto_reverse(&self) -> bool {
1213 self.auto_reverse
1214 }
1215
1216 pub fn should_apply_folded(&self) -> bool {
1221 self.apply_when_finished
1222 || match self.state_kind() {
1223 AnimStateKind::Paused => true,
1224 AnimStateKind::Idle
1225 | AnimStateKind::Stopped
1226 | AnimStateKind::PassInProgress
1227 | AnimStateKind::PassFinished
1228 | AnimStateKind::Completed => false,
1229 }
1230 }
1231
1232 pub fn apply_folded(&self, computed_style: &mut Style) {
1237 computed_style.apply_mut(&self.folded_style);
1238 }
1239}