@@ -591,15 +591,16 @@ impl Animation {
591591 return ;
592592 }
593593
594- let total_progress = match self . state {
594+ // Raw progress ratio of the animation: can be negative (before start) or >1.0 (after end).
595+ let progress = match self . state {
595596 AnimationState :: Running | AnimationState :: Pending | AnimationState :: Finished => {
596597 ( now - self . started_at ) / self . duration
597598 } ,
598599 AnimationState :: Paused ( progress) => progress,
599600 AnimationState :: Canceled => return ,
600601 } ;
601602
602- if total_progress < 0.
603+ if progress < 0.
603604 && self . fill_mode != AnimationFillMode :: Backwards
604605 && self . fill_mode != AnimationFillMode :: Both
605606 {
@@ -611,9 +612,6 @@ impl Animation {
611612 {
612613 return ;
613614 }
614- let total_progress = total_progress
615- . min ( self . current_iteration_end_progress ( ) )
616- . max ( 0.0 ) ;
617615
618616 // If we only need to take into account one keyframe, then exit early
619617 // in order to avoid doing more work.
@@ -623,8 +621,27 @@ impl Animation {
623621 }
624622 } ;
625623
624+ // Handle negative progress (before animation start) with backwards/both fill mode
625+ if progress < 0.0 {
626+ let keyframe = match self . current_direction {
627+ AnimationDirection :: Normal => self . computed_steps . first ( ) . unwrap ( ) ,
628+ AnimationDirection :: Reverse => self . computed_steps . last ( ) . unwrap ( ) ,
629+ _ => unreachable ! ( ) ,
630+ } ;
631+ add_declarations_to_map ( keyframe) ;
632+ return ;
633+ }
634+
635+ // Progress clamped to the current iteration (0.0 to 1.0).
636+ let total_progress = progress. min ( self . current_iteration_end_progress ( ) ) . max ( 0.0 ) ;
637+
626638 if total_progress >= 1.0 {
627- add_declarations_to_map ( self . computed_steps . last ( ) . unwrap ( ) ) ;
639+ let keyframe = match self . current_direction {
640+ AnimationDirection :: Normal => self . computed_steps . last ( ) . unwrap ( ) ,
641+ AnimationDirection :: Reverse => self . computed_steps . first ( ) . unwrap ( ) ,
642+ _ => unreachable ! ( ) ,
643+ } ;
644+ add_declarations_to_map ( keyframe) ;
628645 return ;
629646 }
630647
@@ -662,26 +679,24 @@ impl Animation {
662679 _ => unreachable ! ( ) ,
663680 }
664681
665- debug ! (
666- "Animation::get_property_declaration_at_time: keyframe from {:?} to {:?}" ,
667- prev_keyframe_index, next_keyframe_index
668- ) ;
669-
670682 let prev_keyframe = & self . computed_steps [ prev_keyframe_index] ;
671683 let next_keyframe = match next_keyframe_index {
672684 Some ( index) => & self . computed_steps [ index] ,
673685 None => {
686+ add_declarations_to_map ( & prev_keyframe) ;
674687 return ;
675688 } ,
676689 } ;
677- if total_progress < 0.0 {
690+
691+ // Detect zero interval. Prevent division by zero from percentage_between_keyframes.
692+ if Some ( prev_keyframe_index) == next_keyframe_index {
678693 add_declarations_to_map ( & prev_keyframe) ;
679694 return ;
680695 }
681696
682- // At progress 0, we need to handle step functions specially
697+ // At progress 0 (start of normal direction) , we need to handle step functions specially
683698 // for "jump-both, jump-start, start" step functions.
684- if total_progress == 0.0 {
699+ if total_progress == 0.0 && self . current_direction == AnimationDirection :: Normal {
685700 if let TimingFunction :: Steps ( _steps, pos) = & prev_keyframe. timing_function {
686701 if * pos == StepPosition :: JumpBoth
687702 || * pos == StepPosition :: JumpStart
0 commit comments