Skip to content

Commit 1038777

Browse files
committed
chain almost fixed
1 parent 7ba6c3c commit 1038777

File tree

6 files changed

+99
-71
lines changed

6 files changed

+99
-71
lines changed

examples/demos/flip-card/index.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ export default function Card() {
1313
opacity: flipped ? 1 : 0,
1414
transform: `perspective(1400px) rotateX(0deg)`,
1515
},
16-
/*[
16+
to: [
1717
{ transform: `perspective(1400px) rotateX(45deg)` },
1818
{ transform: `perspective(1400px) rotateX(${flipped ? 180 : 0}deg)` },
19-
],*/
20-
to: async next => {
19+
],
20+
/*async next => {
2121
await next({ transform: `perspective(1400px) rotateX(45deg)` })
2222
await next(
2323
{
2424
transform: `perspective(1400px) rotateX(${flipped ? 180 : 0}deg)`,
2525
},
2626
true
2727
)
28-
},
28+
},*/
2929
//onRest: v => console.log(v),
3030
})
3131
return (

examples/demos/list-reordering/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import React, { useState } from 'react'
1+
import React, { useState, useEffect } from 'react'
22
import { useTransition, animated } from 'react-spring/hooks'
33
import shuffle from 'lodash/shuffle'
44
import data from './data'
55
import './styles.css'
66

77
export default function App() {
88
const [rows, set] = useState(data)
9+
useEffect(() => void setInterval(() => set(shuffle), 3000), [])
910

1011
let height = 0
1112
const transitions = useTransition({
@@ -22,7 +23,7 @@ export default function App() {
2223
})
2324

2425
return (
25-
<div className="list-reorder-scroll" onClick={() => set(shuffle)}>
26+
<div className="list-reorder-scroll">
2627
<div className="list-reorder" style={{ height: height + 15 }}>
2728
{transitions.map(({ item, props: { y, ...rest }, key }, index) => (
2829
<animated.div

examples/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import Demo from './components/Demo'
55
import examples from './components/examples-hooks'
66
import './styles.css'
77

8-
//const DEBUG = false
9-
const DEBUG = 'chain'
8+
const DEBUG = false
9+
//const DEBUG = 'reorder'
1010

1111
ReactDOM.render(
1212
<DemoGrid fullscreen={!!DEBUG}>

src/animated/Controller.js

Lines changed: 74 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -27,55 +27,23 @@ export default class Controller {
2727
this.lastTime = undefined
2828
this.guid = 0
2929
this.local = 0
30+
this.async = undefined
3031
this.update(props)
3132
}
3233

3334
update(props = {}) {
3435
let { from = {}, to = {}, ...rest } = interpolateTo(props)
3536
let isArray = is.arr(to)
3637
let isFunction = is.fun(to)
37-
let queue = Promise.resolve()
38-
39-
if (isArray) {
40-
for (let i = 0; i < to.length; i++) {
41-
const index = i
42-
const last = index === to.length - 1
43-
const fresh = { ...props, to: to[index] }
44-
if (!last) fresh.onRest = undefined
45-
queue = queue.then(
46-
() => this.local === this.guid && this.update(interpolateTo(fresh))
47-
)
48-
}
49-
} else if (isFunction) {
50-
let index = 0
51-
let fn = to
52-
queue = queue.then(
53-
() =>
54-
new Promise(res =>
55-
fn(
56-
// Next
57-
(p, last = false) => {
58-
if (this.local === this.guid) {
59-
const fresh = { ...props, ...interpolateTo(p), config }
60-
if (!last) fresh.onRest = undefined
61-
if (is.arr(fresh.config)) fresh.config = fresh.config[index]
62-
index++
63-
return this.update(fresh).then(() => last && res())
64-
}
65-
},
66-
// Cancel
67-
() => this.stop({ finished: true })
68-
)
69-
)
70-
)
71-
}
7238

73-
// If "to" is either a function or an array it will be processed async, therefor "to" should be empty right now
74-
// If the view relies on certain values "from" has to be present
39+
// Test again async "to"
7540
if (isArray || isFunction) {
7641
this.local = ++this.guid
42+
// If "to" is either a function or an array it will be processed async, therefor "to" should be empty right now
43+
// If the view relies on certain values "from" has to be present
44+
this.async = { to, props }
7745
to = {}
78-
}
46+
} else this.async = undefined
7947

8048
this.props = { ...this.props, ...rest }
8149
let {
@@ -194,32 +162,82 @@ export default class Controller {
194162
}
195163
}
196164

197-
// Do not attempt to start if this is a referenced controller
198-
if (ref) return
199-
// Either return the async queue, or start a new animation
200-
return isArray || isFunction ? queue : this.start()
165+
return this.start()
201166
}
202167

203-
start() {
204-
this.startTime = now()
205-
if (this.isActive) this.stop()
206-
this.isActive = true
207-
if (this.props.onStart) this.props.onStart()
208-
addController(this)
209-
return new Promise(res => (this.resolve = res))
168+
// When the controller is references, it won't start on its own, even thought every update calls start and receives
169+
// a promise. The animation can then only be started by ref, which enforces it by calling start(true)
170+
start(force = false) {
171+
// This promise tracks the animation until onRest
172+
const promise = new Promise(res => (this.resolve = res))
173+
// Return immediately if the controller bears a reference
174+
if (this.props.ref && force === false) return promise
175+
// Stop all occuring animations (without resetting change-detection)
176+
this.stop()
177+
178+
if (this.async) {
179+
let { to, props } = this.async
180+
let queue = Promise.resolve()
181+
if (is.arr(to)) {
182+
for (let i = 0; i < to.length; i++) {
183+
const index = i
184+
const last = index === to.length - 1
185+
const fresh = { ...props, to: to[index] }
186+
if (is.arr(fresh.config)) fresh.config = fresh.config[index]
187+
if (!last) fresh.onRest = undefined
188+
queue = queue.then(
189+
() => this.local === this.guid && this.update(interpolateTo(fresh))
190+
)
191+
}
192+
} else if (is.fun(to)) {
193+
let index = 0
194+
let fn = to
195+
queue = queue.then(
196+
() =>
197+
new Promise(res =>
198+
fn(
199+
// Next
200+
(p, last = false) => {
201+
if (this.local === this.guid) {
202+
const fresh = { ...props, ...interpolateTo(p) }
203+
if (!last) fresh.onRest = undefined
204+
if (is.arr(fresh.config)) fresh.config = fresh.config[index]
205+
index++
206+
return this.update(fresh).then(() => last && res())
207+
}
208+
},
209+
// Cancel
210+
() => this.stop({ finished: true })
211+
)
212+
)
213+
)
214+
}
215+
return queue.then(this.resolve)
216+
} else {
217+
// Set start time tag
218+
this.startTime = now()
219+
if (this.isActive) this.stop()
220+
this.isActive = true
221+
if (this.props.onStart) this.props.onStart()
222+
addController(this)
223+
}
224+
return promise
210225
}
211226

212227
stop(result = { finished: false }) {
228+
this.isActive = false
213229
removeController(this)
214-
// Reset collected changes
215-
if (result.finished)
230+
231+
if (result.finished) {
232+
// Reset collected changes
216233
getValues(this.animations).forEach(a => (a.changes = undefined))
217-
this.isActive = false
234+
// Call onRest if present
235+
if (this.props.onRest) this.props.onRest(this.merged)
218236

219-
if (this.props.onRest && result.finished) this.props.onRest(this.merged)
220-
if (this.resolve) {
221-
this.resolve(this.merged)
222-
this.resolve = undefined
237+
if (this.resolve) {
238+
this.resolve(this.merged)
239+
this.resolve = undefined
240+
}
223241
}
224242
}
225243

src/useSprings.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const useSprings = (length, props) => {
3434

3535
// The hooks reference api gets defined here ...
3636
const api = useImperativeMethods(ref, () => ({
37-
start: () => Promise.all(ctrl.current.map(c => c.start())),
37+
start: () => Promise.all(ctrl.current.map(c => c.start(true))),
3838
stop: () => ctrl.current.forEach(c => c.stop(/*{ finished: true }*/)),
3939
}))
4040

src/useTransition.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export function useTransition(props) {
7373

7474
// update the map object
7575
const ctrl = instances.current.get(key)
76+
7677
if (name === 'update' || name !== state.current.active[key]) {
7778
state.current.active[key] = name
7879

@@ -87,24 +88,32 @@ export function useTransition(props) {
8788
...extra,
8889
}
8990

91+
//console.log(name, item.name, destroyed)
9092
ctrl.update(newProps).then(() => {
93+
//console.log(' promise.res > ', name, item.name, destroyed)
9194
if (mounted.current) {
9295
if (destroyed && onDestroyed) onDestroyed(item)
93-
// Clean up internal state when items unmount, this doesn't necessrily trigger a forceUpdate
96+
// Clean up internal state when items unmount, this doesn't need to trigger a forceUpdate
9497
if (destroyed) {
9598
delete state.current.active[key]
9699
state.current = {
97100
...state.current,
101+
flagged: true,
98102
deleted: state.current.deleted.filter(t => t.key !== key),
99103
transitions: state.current.transitions.filter(
100104
t => t.key !== key
101105
),
102106
}
107+
}
103108

104-
// Only when everything's come to rest we enforce a complete dom clean-up
105-
const currentInstances = Array.from(instances.current)
106-
if (!currentInstances.some(([, c]) => c.isActive))
107-
requestFrame(() => forceUpdate())
109+
// Only when everything's come to rest we enforce a complete dom clean-up
110+
const currentInstances = Array.from(instances.current)
111+
if (
112+
state.current.flagged &&
113+
!currentInstances.some(([, c]) => c.isActive)
114+
) {
115+
requestFrame(() => forceUpdate())
116+
state.current.flagged = false
108117
}
109118
}
110119
})
@@ -125,7 +134,7 @@ export function useTransition(props) {
125134

126135
useImperativeMethods(ref, () => ({
127136
start: () =>
128-
Promise.all(Array.from(instances.current).map(([, c]) => c.start())),
137+
Promise.all(Array.from(instances.current).map(([, c]) => c.start(true))),
129138
stop: () =>
130139
Array.from(instances.current).forEach(
131140
([, c]) => c.isActive && c.stop(/*{ finished: true }*/)

0 commit comments

Comments
 (0)