@@ -9,6 +9,7 @@ import { useClipId, useClipStart, useProvideClipDuration } from "./clip"
99import { useCurrentFrame } from "./frame"
1010import type { Easing } from "./animation/functions"
1111import { useTimelineClips , type TimelineClip } from "./timeline"
12+ import { recordFrameScriptDebugLog } from "./debug-log"
1213
1314type Lerp < T > = ( from : T , to : T , t : number ) => T
1415
@@ -98,6 +99,8 @@ export type AnimationContext = {
9899type InternalContext = AnimationContext & {
99100 now : number
100101 maxFrame : number
102+ cancelled : boolean
103+ unresolvedClipLabels : Set < string >
101104 register : ( variable : Variable < unknown > ) => void
102105}
103106
@@ -461,32 +464,17 @@ export const useAnimation = (
461464 if ( ! clipId ) return true
462465 return isDescendantOf ( clip , clipId )
463466 }
464- for ( const clip of clips ) {
465- if ( ! clip . label ) continue
466- const existing = map . get ( clip . label )
467- if ( ! existing ) {
468- map . set ( clip . label , clip )
469- continue
470- }
471- const nextInScope = isInScope ( clip )
472- const existingInScope = isInScope ( existing )
473- if ( nextInScope && ! existingInScope ) {
474- map . set ( clip . label , clip )
475- continue
476- }
477- if ( nextInScope === existingInScope ) {
478- if ( clip . start < existing . start ) {
479- map . set ( clip . label , clip )
480- continue
481- }
482- if ( clip . start === existing . start ) {
483- const clipDepth = clip . depth ?? 0
484- const existingDepth = existing . depth ?? 0
485- if ( clipDepth < existingDepth ) {
486- map . set ( clip . label , clip )
487- }
488- }
489- }
467+ const candidates = clips
468+ . filter ( ( clip ) => clip . label && ! clip . pending && isInScope ( clip ) )
469+ . sort ( ( a , b ) => {
470+ if ( a . start !== b . start ) return a . start - b . start
471+ const depthDelta = ( a . depth ?? 0 ) - ( b . depth ?? 0 )
472+ if ( depthDelta !== 0 ) return depthDelta
473+ return a . id . localeCompare ( b . id )
474+ } )
475+ for ( const clip of candidates ) {
476+ if ( ! clip . label || map . has ( clip . label ) ) continue
477+ map . set ( clip . label , clip )
490478 }
491479 return map
492480 } , [ clips , clipId ] )
@@ -527,7 +515,10 @@ export const useAnimation = (
527515 const internal : InternalContext = {
528516 now : 0 ,
529517 maxFrame : 0 ,
518+ cancelled : false ,
519+ unresolvedClipLabels : new Set ( ) ,
530520 register : ( variable ) => {
521+ if ( internal . cancelled ) return
531522 if (
532523 variable . _state . ownerId != null &&
533524 variable . _state . ownerId !== ownerId
@@ -549,18 +540,77 @@ export const useAnimation = (
549540 return new AnimationHandle ( internal , target )
550541 } ,
551542 waitUntilClip : ( label : string ) => {
543+ if ( internal . cancelled ) {
544+ return new AnimationHandle ( internal , internal . now )
545+ }
552546 const clip = clipLabelMap . get ( label )
553547 if ( ! clip ) {
548+ internal . unresolvedClipLabels . add ( label )
549+ recordFrameScriptDebugLog ( "animation" , "waitUntilClip:unresolved" , {
550+ label,
551+ ownerId,
552+ runId,
553+ clipId,
554+ clipStart,
555+ now : internal . now ,
556+ labels : Array . from ( clipLabelMap . keys ( ) ) ,
557+ sameLabelClips : clips
558+ . filter ( ( item ) => item . label === label )
559+ . map ( ( item ) => ( {
560+ id : item . id ,
561+ start : item . start ,
562+ end : item . end ,
563+ depth : item . depth ,
564+ parentId : item . parentId ,
565+ pending : item . pending ,
566+ } ) ) ,
567+ } )
554568 return new AnimationHandle ( internal , internal . now )
555569 }
556570 const targetFrame = clip . start - clipStart
557571 const target = Math . max ( internal . now , toFrames ( targetFrame ) )
572+ recordFrameScriptDebugLog ( "animation" , "waitUntilClip:resolved" , {
573+ label,
574+ ownerId,
575+ runId,
576+ clipId,
577+ clipStart,
578+ now : internal . now ,
579+ targetFrame,
580+ target,
581+ chosen : {
582+ id : clip . id ,
583+ start : clip . start ,
584+ end : clip . end ,
585+ depth : clip . depth ,
586+ parentId : clip . parentId ,
587+ pending : clip . pending ,
588+ } ,
589+ sameLabelClips : clips
590+ . filter ( ( item ) => item . label === label )
591+ . map ( ( item ) => ( {
592+ id : item . id ,
593+ start : item . start ,
594+ end : item . end ,
595+ depth : item . depth ,
596+ parentId : item . parentId ,
597+ pending : item . pending ,
598+ } ) ) ,
599+ } )
558600 return new AnimationHandle ( internal , target )
559601 } ,
560602 move : ( variable ) => {
603+ if ( internal . cancelled ) {
604+ return {
605+ to : ( ) => new AnimationHandle ( internal , internal . now ) ,
606+ }
607+ }
561608 internal . register ( variable as Variable < unknown > )
562609 return {
563610 to : ( value , durationFrames , easing ) => {
611+ if ( internal . cancelled ) {
612+ return new AnimationHandle ( internal , internal . now )
613+ }
564614 if ( isDev ) {
565615 assertCompatibleValue ( variable . _state . kind , value )
566616 }
@@ -576,6 +626,16 @@ export const useAnimation = (
576626 to : value as VariableType ,
577627 easing,
578628 } )
629+ recordFrameScriptDebugLog ( "animation" , "move" , {
630+ ownerId,
631+ runId,
632+ clipId,
633+ clipStart,
634+ start,
635+ end,
636+ from,
637+ to : value ,
638+ } )
579639 return new AnimationHandle ( internal , end + 1 )
580640 } ,
581641 }
@@ -592,17 +652,78 @@ export const useAnimation = (
592652 }
593653
594654 const execute = async ( ) => {
655+ recordFrameScriptDebugLog ( "animation" , "run:start" , {
656+ ownerId,
657+ runId,
658+ clipId,
659+ clipStart,
660+ clipStartContext,
661+ clipCount : clips . length ,
662+ labels : Array . from ( clipLabelMap . entries ( ) ) . map ( ( [ label , clip ] ) => ( {
663+ label,
664+ id : clip . id ,
665+ start : clip . start ,
666+ end : clip . end ,
667+ depth : clip . depth ,
668+ parentId : clip . parentId ,
669+ pending : clip . pending ,
670+ } ) ) ,
671+ } )
595672 try {
596673 await run ( internal )
597674 } finally {
598675 // keep owner
599676 }
600677
601678 if ( runIdRef . current !== runId ) {
679+ recordFrameScriptDebugLog ( "animation" , "run:stale" , {
680+ ownerId,
681+ runId,
682+ currentRunId : runIdRef . current ,
683+ clipId,
684+ clipStart,
685+ now : internal . now ,
686+ maxFrame : internal . maxFrame ,
687+ } )
688+ finalize ( )
689+ return
690+ }
691+ if ( internal . unresolvedClipLabels . size > 0 ) {
692+ for ( const variable of variablesRef . current ) {
693+ if ( variable . _state . ownerId === ownerId ) {
694+ variable . _state . segments . length = 0
695+ }
696+ }
697+ if ( isDev ) {
698+ console . warn (
699+ `useAnimation: waiting for clip label(s): ${ Array . from (
700+ internal . unresolvedClipLabels ,
701+ ) . join ( ", " ) } `,
702+ )
703+ }
704+ setReady ( false )
705+ recordFrameScriptDebugLog ( "animation" , "run:unresolved" , {
706+ ownerId,
707+ runId,
708+ clipId,
709+ clipStart,
710+ unresolvedClipLabels : Array . from ( internal . unresolvedClipLabels ) ,
711+ now : internal . now ,
712+ maxFrame : internal . maxFrame ,
713+ } )
602714 finalize ( )
603715 return
604716 }
605717 const nextDuration = Math . max ( 1 , Math . round ( internal . maxFrame ) )
718+ recordFrameScriptDebugLog ( "animation" , "run:finish" , {
719+ ownerId,
720+ runId,
721+ clipId,
722+ clipStart,
723+ now : internal . now ,
724+ maxFrame : internal . maxFrame ,
725+ durationFrames : nextDuration ,
726+ } )
606727 setDurationFrames ( nextDuration )
607728 setReady ( true )
608729 finalize ( )
@@ -612,6 +733,7 @@ export const useAnimation = (
612733
613734 return ( ) => {
614735 runIdRef . current += 1
736+ internal . cancelled = true
615737 for ( const variable of variablesRef . current ) {
616738 if ( variable . _state . ownerId === ownerId ) {
617739 variable . _state . ownerId = null
0 commit comments