Skip to content

Commit 923a1c1

Browse files
committed
wip: round 3
1 parent 6fa673c commit 923a1c1

File tree

1 file changed

+45
-36
lines changed

1 file changed

+45
-36
lines changed

src/animated/Controller.ts

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ import { SpringProps } from '../../types/renderprops'
1414

1515
type Animation = any
1616
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+
}
1823

1924
type UpdateProps<DS extends object> = DS &
2025
SpringProps<DS> & {
@@ -31,7 +36,7 @@ class Controller<DS extends object = any> {
3136
props: UpdateProps<DS> = {} as any
3237
merged: DS = {} as any
3338
values: DS = {} as any
34-
interpolations: InterpolationMap = {}
39+
interpolations: InterpolationMap<DS> = {} as any
3540
animations: AnimationMap = {}
3641
configs: any[] = []
3742
queue: any[] = []
@@ -41,28 +46,30 @@ class Controller<DS extends object = any> {
4146

4247
getValues = () => this.interpolations
4348

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>) {
4854
// 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
5056

5157
// If config is either a function or an array, queue it up as is
5258
if (is.arr(to) || is.fun(to)) {
53-
this.queue.push({ ...props, delay, to })
59+
this.queue.push({ ...restProps, delay, to })
5460
}
5561
// Otherwise go through each key since it could be delayed individually
5662
else if (to) {
5763
let ops: any[] = []
5864
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 },
6673
}
6774
})
6875
ops.forEach(op => this.queue.push(op))
@@ -72,14 +79,15 @@ class Controller<DS extends object = any> {
7279
this.queue.sort((a, b) => a.delay - b.delay)
7380

7481
// 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)
7683

7784
return this
7885
}
7986

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+
*/
8391
start(onEnd?: OnEnd) {
8492
// If a queue is present we must execute it
8593
if (this.queue.length) {
@@ -91,7 +99,10 @@ class Controller<DS extends object = any> {
9199
if (is.obj(from)) this.merged = { ...from, ...this.merged }
92100
if (is.obj(to)) this.merged = { ...this.merged, ...to }
93101
})
102+
// Reset any queue-related state
94103
prevQueue.length = 0
104+
this.pendingCount = 0
105+
this.onEndQueue.length = 0
95106
}
96107

97108
// 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> {
100111
const queue = (this.prevQueue = this.queue)
101112
this.queue = prevQueue
102113

103-
// Reset any queue-related state
104-
this.pendingCount = 0
105-
this.onEndQueue.length = 0
106-
107114
// 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.
109116
let remaining = queue.length
110117
const didEnd =
111118
onEnd &&
112119
((finished?: boolean) => {
113-
if (--remaining < 0 || guid !== this.guid) return
114-
onEnd(finished)
120+
if (--remaining === 0) onEnd(finished)
115121
})
116122

117123
// Go through each entry and execute it
@@ -159,17 +165,21 @@ class Controller<DS extends object = any> {
159165
this.props = {} as any
160166
this.merged = {} as any
161167
this.values = {} as any
162-
this.interpolations = {}
168+
this.interpolations = {} as any
163169
this.animations = {}
164170
this.configs = []
165171
}
166172

167173
// Add this controller to the frameloop
168174
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)
173183
}
174184
}
175185

@@ -401,16 +411,15 @@ class Controller<DS extends object = any> {
401411
)
402412

403413
if (changed) {
404-
// Make animations available to frameloop
405-
this.configs = Object.values(this.animations)
406-
407-
this.interpolations = {}
408414
const values = (this.values = {} as any)
415+
const interpolations = (this.interpolations = {} as any)
409416
for (const key in this.animations) {
410417
const { interpolation } = this.animations[key]
411-
this.interpolations[key] = interpolation
412418
values[key] = interpolation.getValue()
419+
interpolations[key] = interpolation
413420
}
421+
// Make animations available to frameloop
422+
this.configs = Object.values(this.animations)
414423
}
415424
return this
416425
}

0 commit comments

Comments
 (0)