Skip to content

Commit d1773bc

Browse files
committed
useTrail cleanup
1 parent aa3fd1c commit d1773bc

File tree

5 files changed

+69
-82
lines changed

5 files changed

+69
-82
lines changed

examples/demos/hooks/keyframes-blackflag/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ const interp = i => r =>
88
`translate3d(0, ${15 * Math.sin(r + (i * 2 * Math.PI) / 1.6)}px, 0)`
99
const useScript = useKeyframes.spring(
1010
async next => {
11-
while (1) await next({ radians: 2 * Math.PI, config: { duration: 3500 }, reset: true })
11+
while (1) await next({ radians: 2 * Math.PI, config: { duration: 3500 } })
1212
},
13-
{ from: { radians: 0 } }
13+
{ from: { radians: 0 }, reset: true }
1414
)
1515

1616
export default function App() {

examples/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import examples from './components/examples-hooks'
66
import './styles.css'
77

88
const DEBUG = false
9-
//const DEBUG = "chain-anim"
9+
//const DEBUG = "flag"
1010

1111
ReactDOM.render(
1212
<DemoGrid>

src/hooks/useChain.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect } from 'react'
1+
import { useEffect } from 'react'
22

33
export function useChain(args) {
44
useEffect(() => {

src/hooks/useSpring.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
useEffect,
66
useCallback,
77
} from 'react'
8-
import Controller from '../animated/Controller'
8+
import Ctrl from '../animated/Controller'
99
import { requestFrame } from '../animated/Globals'
1010

1111
export function useSpring(args) {
@@ -14,7 +14,7 @@ export function useSpring(args) {
1414
const isFn = typeof args === 'function'
1515
const { onRest, onKeyframesHalt, ...props } = isFn ? args() : args
1616
// The controller maintains the animation values, starts and tops animations
17-
const [ctrl] = useState(() => new Controller(props))
17+
const [ctrl] = useState(() => new Ctrl(props))
1818
// Define onEnd callbacks and resolvers
1919
const endResolver = useRef(null)
2020
const onHalt = onKeyframesHalt
@@ -36,8 +36,8 @@ export function useSpring(args) {
3636
const updateCtrl = useCallback(
3737
updateProps => {
3838
ctrl.update(updateProps)
39-
if (!props.ref) ctrl.start(onHalt)
40-
if (props.reset) requestFrame(forceUpdate)
39+
if (!ctrl.props.ref) ctrl.start(onHalt)
40+
if (ctrl.props.reset) requestFrame(forceUpdate)
4141
},
4242
[onRest, onKeyframesHalt, props.ref]
4343
)

src/hooks/useTrail.js

Lines changed: 61 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,77 @@
1-
import React from 'react'
2-
import Controller from '../animated/Controller'
3-
import * as Globals from '../animated/Globals'
1+
import {
2+
useRef,
3+
useState,
4+
useMemo,
5+
useCallback,
6+
useImperativeMethods,
7+
useEffect,
8+
} from 'react'
9+
import Ctrl from '../animated/Controller'
10+
import { requestFrame } from '../animated/Globals'
411

5-
const map = new Map([])
6-
export function useTrail (count, params) {
7-
const isFunctionProps = typeof params === 'function'
8-
const {
9-
delay,
10-
reverse,
11-
onKeyframesHalt = () => null,
12-
onRest,
13-
...props
14-
} = isFunctionProps ? params() : params
15-
const instances = React.useRef(map)
16-
const mounted = React.useRef(false)
17-
const endResolver = React.useRef()
18-
const [, forceUpdate] = React.useState()
19-
20-
const onHalt = onRest
12+
export function useTrail(length, args) {
13+
const [, forceUpdate] = useState()
14+
// Extract animation props and hook-specific props, can be a function or an obj
15+
const isFn = typeof args === 'function'
16+
const { reverse, onKeyframesHalt, onRest, ...props } = isFn ? args() : args
17+
// The controller maintains the animation values, starts and tops animations
18+
const instances = useMemo(
19+
() => {
20+
const instances = []
21+
for (let i = 0; i < length; i++)
22+
instances.push(
23+
new Ctrl({ ...props, attach: i > 0 && (() => instances[i - 1]) })
24+
)
25+
return instances
26+
},
27+
[length]
28+
)
29+
// Define onEnd callbacks and resolvers
30+
const endResolver = useRef()
31+
const onHalt = onKeyframesHalt
2132
? ctrl => ({ finished }) => {
22-
finished && endResolver.current && endResolver.current()
23-
finished && mounted.current && onRest(ctrl.merged)
24-
}
25-
: onKeyframesHalt
26-
27-
if (count > instances.current.size) {
28-
for (let i = instances.current.size; i < count; i++) {
29-
instances.current.set(
30-
i,
31-
new Controller({
32-
...props,
33-
attach: i === 0 ? undefined : () => instances.current.get(i - 1)
34-
})
35-
)
36-
}
37-
}
38-
39-
const update = React.useCallback(
40-
/** resolve and last are passed to the update function from the keyframes controller */
41-
props => {
42-
for (let [idx, ctrl] of instances.current.entries()) {
43-
ctrl.update(props)
44-
if (!props.ref) {
45-
ctrl.start(instances.current.size - 1 === idx && onHalt(ctrl))
33+
if (finished) {
34+
if (endResolver.current) endResolver.current()
35+
if (onRest) onRest(ctrl.merged)
4636
}
4737
}
48-
Globals.requestFrame(() => props.reset && forceUpdate())
49-
},
50-
[onRest]
51-
)
38+
: onKeyframesHalt || (() => null)
5239

53-
React.useImperativeMethods(props.ref, () => ({
40+
// The hooks explcit API gets defined here ...
41+
useImperativeMethods(props.ref, () => ({
5442
start: resolve => {
5543
endResolver.current = resolve
56-
for (let [idx, ctrl] of instances.current.entries()) {
57-
ctrl.start(instances.current.size - 1 === idx && onHalt(ctrl))
58-
}
44+
instances.forEach((ctrl, i) =>
45+
ctrl.start(instances.length - 1 === i && onHalt(ctrl))
46+
)
5947
},
60-
tag: 'TrailHook'
6148
}))
6249

63-
/** must hoooks always return something? */
64-
React.useEffect(() => {
65-
mounted.current = true
66-
return () => void (mounted.current = false)
67-
}, [])
68-
69-
React.useLayoutEffect(() => void (!isFunctionProps && update(props)))
70-
71-
const propValues = Array.from(instances.current.values()).reduce(
72-
(acc, ctrl) => {
73-
reverse ? acc.unshift(ctrl.getValues()) : acc.push(ctrl.getValues())
74-
return acc
50+
// Defines the hooks setter, which updates the controller
51+
const updateCtrl = useCallback(
52+
props => {
53+
instances.forEach((ctrl, i) => {
54+
const last = instances.length - 1 === i
55+
ctrl.update(props)
56+
if (!ctrl.props.ref) ctrl.start(last && onHalt(ctrl))
57+
if (last && ctrl.props.reset) requestFrame(forceUpdate)
58+
})
7559
},
76-
[]
60+
[onRest, onKeyframesHalt, props.ref]
7761
)
7862

79-
return isFunctionProps
63+
// Update next frame is props aren't functional
64+
useEffect(() => void (!isFn && updateCtrl(props)))
65+
// Return animated props, or, anim-props + the update-setter above
66+
const propValues = instances.reduce((acc, ctrl) => {
67+
reverse ? acc.unshift(ctrl.getValues()) : acc.push(ctrl.getValues())
68+
return acc
69+
}, [])
70+
return isFn
8071
? [
81-
propValues,
82-
props => update(props),
83-
(finished = false) => {
84-
for (let [, ctrl] of instances.current.entries()) {
85-
ctrl.stop(finished)
86-
}
87-
}
88-
]
72+
propValues,
73+
updateCtrl,
74+
(finished = false) => instances.forEach(ctrl => ctrl.stop(finished)),
75+
]
8976
: propValues
9077
}

0 commit comments

Comments
 (0)