@@ -14,7 +14,12 @@ import { SpringProps } from '../../types/renderprops'
14
14
15
15
type Animation = any
16
16
type AnimationMap = { [ name : string ] : Animation }
17
- type InterpolationMap = { [ name : string ] : AnimatedValue | AnimatedValueArray }
17
+
18
+ type InterpolationMap < DS > = {
19
+ [ K in keyof DS ] : DS [ K ] extends ArrayLike < any >
20
+ ? AnimatedValueArray
21
+ : AnimatedValue
22
+ }
18
23
19
24
type UpdateProps < DS extends object > = DS &
20
25
SpringProps < DS > & {
@@ -31,7 +36,7 @@ class Controller<DS extends object = any> {
31
36
props : UpdateProps < DS > = { } as any
32
37
merged : DS = { } as any
33
38
values : DS = { } as any
34
- interpolations : InterpolationMap = { }
39
+ interpolations : InterpolationMap < DS > = { } as any
35
40
animations : AnimationMap = { }
36
41
configs : any [ ] = [ ]
37
42
queue : any [ ] = [ ]
@@ -41,28 +46,30 @@ class Controller<DS extends object = any> {
41
46
42
47
getValues = ( ) => this . interpolations
43
48
44
- /** update(props)
45
- * This function filters input props and creates an array of tasks which are executed in .start()
46
- * Each task is allowed to carry a delay, which means it can execute asnychroneously */
47
- update ( args : UpdateProps < DS > ) {
49
+ /**
50
+ * Update the controller by merging the given props into an array of tasks.
51
+ * Individual tasks may be async and/or delayed.
52
+ */
53
+ update ( props : UpdateProps < DS > ) {
48
54
// Extract delay and the to-prop from props
49
- const { delay = 0 , to, ...props } = interpolateTo ( args ) as any
55
+ const { delay = 0 , to, ...restProps } = interpolateTo ( props ) as any
50
56
51
57
// If config is either a function or an array, queue it up as is
52
58
if ( is . arr ( to ) || is . fun ( to ) ) {
53
- this . queue . push ( { ...props , delay, to } )
59
+ this . queue . push ( { ...restProps , delay, to } )
54
60
}
55
61
// Otherwise go through each key since it could be delayed individually
56
62
else if ( to ) {
57
63
let ops : any [ ] = [ ]
58
64
Object . entries ( to ) . forEach ( ( [ k , v ] ) => {
59
- // Fetch delay and create an entry, consisting of the to-props, the delay, and basic props
60
- const entry = { to : { [ k ] : v } , delay : callProp ( delay , k ) , ...props }
61
- const previous = ops [ entry . delay ] && ops [ entry . delay ] . to
62
- ops [ entry . delay ] = {
63
- ...ops [ entry . delay ] ,
64
- ...entry ,
65
- to : { ...previous , ...entry . to } ,
65
+ // Merge entries with the same delay
66
+ const dt = callProp ( delay , k )
67
+ const previous = ops [ dt ] || { }
68
+ ops [ dt ] = {
69
+ ...previous ,
70
+ ...restProps ,
71
+ delay : dt ,
72
+ to : { ...previous . to , [ k ] : v } ,
66
73
}
67
74
} )
68
75
ops . forEach ( op => this . queue . push ( op ) )
@@ -72,14 +79,15 @@ class Controller<DS extends object = any> {
72
79
this . queue . sort ( ( a , b ) => a . delay - b . delay )
73
80
74
81
// Diff the reduced props immediately (they'll contain the from-prop and some config)
75
- if ( hasKeys ( props ) ) this . _diff ( props )
82
+ if ( hasKeys ( restProps ) ) this . _diff ( restProps )
76
83
77
84
return this
78
85
}
79
86
80
- /** start()
81
- * This function either executes a queue, if present, or starts the frameloop, which animates.
82
- * The `useSpring` hooks never have > 1 update per call, because they call this every render. */
87
+ /**
88
+ * Execute any queued updates, else make sure the frameloop is running.
89
+ * The `useSpring` hooks never have > 1 update per call, because they call this every render.
90
+ */
83
91
start ( onEnd ?: OnEnd ) {
84
92
// If a queue is present we must execute it
85
93
if ( this . queue . length ) {
@@ -91,7 +99,10 @@ class Controller<DS extends object = any> {
91
99
if ( is . obj ( from ) ) this . merged = { ...from , ...this . merged }
92
100
if ( is . obj ( to ) ) this . merged = { ...this . merged , ...to }
93
101
} )
102
+ // Reset any queue-related state
94
103
prevQueue . length = 0
104
+ this . pendingCount = 0
105
+ this . onEndQueue . length = 0
95
106
}
96
107
97
108
// The guid helps when tracking frames, a new queue over an old one means an override.
@@ -100,18 +111,13 @@ class Controller<DS extends object = any> {
100
111
const queue = ( this . prevQueue = this . queue )
101
112
this . queue = prevQueue
102
113
103
- // Reset any queue-related state
104
- this . pendingCount = 0
105
- this . onEndQueue . length = 0
106
-
107
114
// Never assume that the last update always finishes last, since that's
108
- // not true when 2+ async animations have indeterminate durations.
115
+ // not true when 2+ async updates have indeterminate durations.
109
116
let remaining = queue . length
110
117
const didEnd =
111
118
onEnd &&
112
119
( ( finished ?: boolean ) => {
113
- if ( -- remaining < 0 || guid !== this . guid ) return
114
- onEnd ( finished )
120
+ if ( -- remaining === 0 ) onEnd ( finished )
115
121
} )
116
122
117
123
// Go through each entry and execute it
@@ -159,17 +165,21 @@ class Controller<DS extends object = any> {
159
165
this . props = { } as any
160
166
this . merged = { } as any
161
167
this . values = { } as any
162
- this . interpolations = { }
168
+ this . interpolations = { } as any
163
169
this . animations = { }
164
170
this . configs = [ ]
165
171
}
166
172
167
173
// Add this controller to the frameloop
168
174
private _start ( onEnd ?: OnEnd ) {
169
- if ( onEnd ) this . onEndQueue . push ( onEnd )
170
- if ( this . idle ) {
171
- this . idle = false
172
- start ( this )
175
+ if ( this . configs . length ) {
176
+ if ( onEnd ) this . onEndQueue . push ( onEnd )
177
+ if ( this . idle ) {
178
+ this . idle = false
179
+ start ( this )
180
+ }
181
+ } else if ( onEnd ) {
182
+ onEnd ( true )
173
183
}
174
184
}
175
185
@@ -401,16 +411,15 @@ class Controller<DS extends object = any> {
401
411
)
402
412
403
413
if ( changed ) {
404
- // Make animations available to frameloop
405
- this . configs = Object . values ( this . animations )
406
-
407
- this . interpolations = { }
408
414
const values = ( this . values = { } as any )
415
+ const interpolations = ( this . interpolations = { } as any )
409
416
for ( const key in this . animations ) {
410
417
const { interpolation } = this . animations [ key ]
411
- this . interpolations [ key ] = interpolation
412
418
values [ key ] = interpolation . getValue ( )
419
+ interpolations [ key ] = interpolation
413
420
}
421
+ // Make animations available to frameloop
422
+ this . configs = Object . values ( this . animations )
414
423
}
415
424
return this
416
425
}
0 commit comments