File tree Expand file tree Collapse file tree 2 files changed +47
-1
lines changed
packages/framer-motion/src/animation/animate Expand file tree Collapse file tree 2 files changed +47
-1
lines changed Original file line number Diff line number Diff line change @@ -337,6 +337,40 @@ describe("animate", () => {
337337 expect ( max ) . toBeGreaterThan ( 2 )
338338 expect ( value . get ( ) ) . toBe ( 0 )
339339 } )
340+
341+ test ( "top-level onComplete fires once when some props are equal and others animate" , async ( ) => {
342+ const calls : number [ ] = [ ]
343+
344+ const proxy = new Proxy (
345+ { x : 0 , y : 0 } ,
346+ {
347+ set : ( target , p : string , newValue : any ) => {
348+ if ( p === "x" || p === "y" ) {
349+ ; ( target as any ) [ p ] = newValue
350+ }
351+ return true
352+ } ,
353+ }
354+ )
355+
356+ const anim = animate (
357+ proxy ,
358+ { x : 100 , y : 0 } ,
359+ {
360+ duration : 0.05 ,
361+ onComplete : ( ) => {
362+ calls . push ( performance . now ( ) )
363+ } ,
364+ }
365+ )
366+
367+ await anim . finished
368+
369+ expect ( calls . length ) . toBe ( 1 )
370+ // And ensure final state reached
371+ expect ( proxy . x ) . toBeGreaterThanOrEqual ( 100 )
372+ expect ( proxy . y ) . toBe ( 0 )
373+ } )
340374} )
341375
342376describe ( "animate: Objects" , ( ) => {
Original file line number Diff line number Diff line change @@ -101,6 +101,7 @@ export function createScopedAnimate(scope?: AnimationScope) {
101101 | DynamicAnimationOptions
102102 ) : AnimationPlaybackControlsWithThen {
103103 let animations : AnimationPlaybackControlsWithThen [ ] = [ ]
104+ let groupOnComplete : VoidFunction | undefined
104105
105106 if ( isSequence ( subjectOrSequence ) ) {
106107 animations = animateSequence (
@@ -109,16 +110,27 @@ export function createScopedAnimate(scope?: AnimationScope) {
109110 scope
110111 )
111112 } else {
113+ // Extract top-level onComplete so it doesn't get applied per-value
114+ const { onComplete, ...rest } = options || { }
115+ if ( typeof onComplete === "function" ) {
116+ groupOnComplete = onComplete as VoidFunction
117+ }
112118 animations = animateSubject (
113119 subjectOrSequence as ElementOrSelector ,
114120 optionsOrKeyframes as DOMKeyframesDefinition ,
115- options as DynamicAnimationOptions ,
121+ rest as DynamicAnimationOptions ,
116122 scope
117123 )
118124 }
119125
120126 const animation = new GroupAnimationWithThen ( animations )
121127
128+ if ( groupOnComplete ) {
129+ animation . finished . then ( ( ) => {
130+ groupOnComplete ?.( )
131+ } )
132+ }
133+
122134 if ( scope ) {
123135 scope . animations . push ( animation )
124136 animation . finished . then ( ( ) => {
You can’t perform that action at this time.
0 commit comments