Skip to content

Commit 07528f4

Browse files
committed
animation middleware, fixAuto, async propTransforms
1 parent ea33a69 commit 07528f4

File tree

8 files changed

+117
-77
lines changed

8 files changed

+117
-77
lines changed

examples/demos/sunburst/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { scaleLinear, scaleSqrt, scaleOrdinal } from 'd3-scale'
77
import { schemeCategory10 as scheme } from 'd3-scale-chromatic'
88
import { interpolate as d3interpolate } from 'd3-interpolate'
99
import { Spring, animated } from 'react-spring'
10-
import Partition from './partition'
10+
import Partition from './Partition'
1111
import data from './data'
1212
import './styles.css'
1313

src/Parallax.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ export default class Parallax extends React.PureComponent {
203203
const { style, children, offset, speed, factor, className, ...props } = this.props
204204
const horizontal = this.context.parallax.props.horizontal
205205
const translate3d = this.animatedTranslate.interpolate({
206-
inputRange: [0, 1],
207-
outputRange: horizontal
206+
range: [0, 1],
207+
output: horizontal
208208
? ['translate3d(0px,0,0)', 'translate3d(1px,0,0)']
209209
: ['translate3d(0,0px,0)', 'translate3d(0,1px,0)'],
210210
})

src/Spring.js

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react'
2+
import ReactDOM from 'react-dom'
23
import PropTypes from 'prop-types'
34
import Animated from './animated/targets/react-dom'
45
import SpringAnimation from './animated/SpringAnimation'
@@ -40,34 +41,46 @@ export default class Spring extends React.PureComponent {
4041
impl: SpringAnimation,
4142
}
4243

43-
constructor(props) {
44-
super()
45-
this._defaultAnimation = new Animated.Value(0)
46-
this._animations = {}
47-
this._updateProps(props, false)
44+
state = { props: undefined }
45+
defaultAnimation = new Animated.Value(0)
46+
animations = {}
47+
48+
componentWillUnmount() {
49+
this.stop()
4850
}
4951

50-
_updateProps({ impl, from, to, config, attach, immediate, reset, onFrame, onRest }, start = false) {
52+
componentWillMount() {
53+
this.updateProps(this.props)
54+
}
55+
56+
componentWillReceiveProps(props) {
57+
this.updateProps(props)
58+
}
59+
60+
async updateProps({ inject, ...props }) {
61+
const { impl, from, to, config, attach, immediate, reset, onFrame, onRest } = inject ? await inject(this, props) : props
62+
5163
const allProps = Object.entries({ ...from, ...to })
52-
const defaultAnimationValue = this._defaultAnimation._value
64+
const defaultAnimationValue = this.defaultAnimation._value
65+
66+
this.defaultAnimation.setValue(0)
67+
this.interpolators = {}
68+
this.animations = allProps.reduce((acc, [name, value], i) => {
69+
const entry = (reset === false && this.animations[name]) || (this.animations[name] = {})
5370

54-
this._interpolators = {}
55-
this._defaultAnimation.setValue(0)
56-
this._animations = allProps.reduce((acc, [name, value], i) => {
57-
const entry = (reset === false && this._animations[name]) || (this._animations[name] = {})
5871
let isNumber = typeof value === 'number'
5972
let isArray = !isNumber && Array.isArray(value)
6073
let fromValue = from[name] !== undefined ? from[name] : value
61-
let toValue = isNumber || isArray ? value : 1
74+
let toValue = isNumber || isArray ? value : 1
6275

6376
if (isNumber && attach) {
6477
// Attach value to target animation
6578
const target = attach(this)
66-
const targetAnimation = target && target._animations[name]
79+
const targetAnimation = target && target.animations[name]
6780
if (targetAnimation) toValue = targetAnimation.animation
6881
}
6982

70-
if (isNumber) {
83+
if (isNumber || toValue === 'auto') {
7184
// Create animated value
7285
entry.animation = entry.interpolation = entry.animation || new Animated.Value(fromValue)
7386
} else if (isArray) {
@@ -76,10 +89,10 @@ export default class Spring extends React.PureComponent {
7689
} else {
7790
// Deal with interpolations
7891
const previous = entry.interpolation && entry.interpolation._interpolation(defaultAnimationValue)
79-
entry.animation = this._defaultAnimation
80-
entry.interpolation = this._defaultAnimation.interpolate({
81-
inputRange: [0, 1],
82-
outputRange: [previous !== undefined ? previous : fromValue, value],
92+
entry.animation = this.defaultAnimation
93+
entry.interpolation = this.defaultAnimation.interpolate({
94+
range: [0, 1],
95+
output: [previous !== undefined ? previous : fromValue, value],
8396
})
8497
}
8598

@@ -89,8 +102,8 @@ export default class Spring extends React.PureComponent {
89102
entry.start = cb => {
90103
Animated.controller(entry.animation, { toValue, ...config }, impl).start(props => {
91104
if (props.finished) {
92-
this._animations[name].stopped = true
93-
if (Object.values(this._animations).every(animation => animation.stopped)) {
105+
this.animations[name].stopped = true
106+
if (Object.values(this.animations).every(animation => animation.stopped)) {
94107
const current = { ...this.props.from, ...this.props.to }
95108
onRest && onRest(current)
96109
cb && cb(current)
@@ -103,54 +116,49 @@ export default class Spring extends React.PureComponent {
103116
entry.animation.stopAnimation()
104117
}
105118

106-
this._interpolators[name] = entry.interpolation
119+
this.interpolators[name] = entry.interpolation
107120
return { ...acc, [name]: entry }
108121
}, {})
109122

110-
if (start) this.start()
123+
const oldAnimatedProps = this.animatedProps
124+
this.animatedProps = new Animated.AnimatedProps(this.interpolators, this.callback)
125+
oldAnimatedProps && oldAnimatedProps.__detach()
111126

112-
var oldPropsAnimated = this._propsAnimated
113-
this._propsAnimated = new Animated.AnimatedProps(this._interpolators, this.callback)
114-
oldPropsAnimated && oldPropsAnimated.__detach()
127+
this.forceUpdate()
128+
this.start()
115129
}
116130

117-
start(props = this.props) {
118-
return new Promise(res => Object.values(this._animations).forEach(animation => animation.start(res)))
131+
start() {
132+
return new Promise(res => this.getAnimations().forEach(animation => animation.start(res)))
119133
}
120134

121135
stop() {
122-
Object.values(this._animations).forEach(animation => animation.stop())
123-
}
124-
125-
update(props) {
126-
this._updateProps({ ...this.props, ...props }, true)
136+
this.getAnimations().forEach(animation => animation.stop())
127137
}
128138

129139
callback = () => {
130-
if (this.props.onFrame) this.props.onFrame(this._propsAnimated.__getValue())
140+
if (this.props.onFrame) this.props.onFrame(this.animatedProps.__getValue())
131141
!this.props.native && this.forceUpdate()
132142
}
133143

134-
componentWillReceiveProps(props) {
135-
this._updateProps(props, true)
136-
}
137-
138-
componentDidMount() {
139-
this.start()
144+
getAnimations() {
145+
return Object.values(this.animations)
140146
}
141147

142-
componentWillUnmount() {
143-
this.stop()
148+
getValues() {
149+
return this.animatedProps ? this.animatedProps.__getValue() : {}
144150
}
145151

146-
getValues() {
147-
return this._propsAnimated.__getValue()
152+
getAnimatedValues() {
153+
return this.props.native ? this.interpolators : this.getValues()
148154
}
149155

150156
render() {
151-
const { children, render, from, to, config, native, ...extra } = this.props
152-
let animatedProps = { ...(native ? this._interpolators : this._propsAnimated.__getValue()), ...extra }
153-
if (render) return render({ ...animatedProps, children })
154-
else return Array.isArray(children) ? children.map(child => child(animatedProps)) : children(animatedProps)
157+
const { children, render, from, to, config, native, inject, ...extra } = this.props
158+
const values = this.getAnimatedValues()
159+
if (Object.keys(values).length) {
160+
const animatedProps = { ...this.getAnimatedValues(), ...extra }
161+
return render ? render({ ...animatedProps, children }) : children(animatedProps)
162+
} else return null
155163
}
156164
}

src/addons/fixAuto.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
import { Value } from 'react-spring'
4+
5+
export default function fixAuto(spring, props) {
6+
return new Promise(res => {
7+
const { native, children, from, to, ...extra } = props
8+
9+
// Create portal
10+
const portal = document.createElement('div')
11+
portal.style.cssText = 'position:static;visibility:hidden;'
12+
document.body.appendChild(portal)
13+
14+
// Call children with to-state
15+
const componentProps = native
16+
? Object.entries({ ...from, ...to }).reduce((acc, [name, value]) => ({ ...acc, [name]: new Value(value) }), {})
17+
: { ...from, ...to }
18+
const result = children({ ...componentProps, ...extra })
19+
20+
// Render to-state vdom to portal
21+
ReactDOM.render(
22+
<div
23+
ref={ref => {
24+
if (ref) {
25+
// Once it's rendered out, fetch bounds
26+
const height = ref.clientHeight
27+
const width = ref.clientWidth
28+
29+
// Remove portal and resolve promise with updated props
30+
document.body.removeChild(portal)
31+
let newToProps = {}
32+
Object.entries(to).forEach(
33+
([name, value]) => value === 'auto' && (newToProps[name] = name === 'height' ? height : width),
34+
)
35+
res({ ...props, to: newToProps })
36+
}
37+
}}>
38+
{result}
39+
</div>,
40+
portal,
41+
)
42+
})
43+
}

src/animated/AnimatedValue.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ export default class AnimatedValue extends AnimatedWithChildren {
5656
}
5757

5858
_flush() {
59-
if (this._animatedStyles.size === 0 || this._tracked) findAnimatedStyles(this, this._animatedStyles)
59+
//if (this._animatedStyles.size === 0 || this._tracked) this._update()
60+
findAnimatedStyles(this, this._animatedStyles)
6061
this._animatedStyles.forEach(animatedStyle => animatedStyle.update())
6162
}
6263

src/animated/Interpolation.js

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ var linear = t => t
99
export default class Interpolation {
1010
static create(config) {
1111
if (typeof config === 'function') return (...args) => config(...args)
12-
if (config.outputRange && typeof config.outputRange[0] === 'string') {
13-
return createInterpolationFromStringOutputRange(config)
14-
}
15-
var outputRange = config.outputRange
16-
var inputRange = config.inputRange
12+
if (config.output && typeof config.output[0] === 'string') return createInterpolationFromStringOutputRange(config)
13+
var outputRange = config.output
14+
var inputRange = config.range
1715
var easing = config.easing || linear
1816
var extrapolateLeft = 'extend'
1917

@@ -49,7 +47,7 @@ export default class Interpolation {
4947

5048
function interpolate(input, inputMin, inputMax, outputMin, outputMax, easing, extrapolateLeft, extrapolateRight) {
5149
var result = input
52-
50+
5351
// Extrapolate
5452
if (result < inputMin) {
5553
if (extrapolateLeft === 'identity') {
@@ -71,14 +69,9 @@ function interpolate(input, inputMin, inputMax, outputMin, outputMax, easing, ex
7169
}
7270
}
7371

74-
if (outputMin === outputMax) {
75-
return outputMin
76-
}
77-
72+
if (outputMin === outputMax) return outputMin
7873
if (inputMin === inputMax) {
79-
if (input <= inputMin) {
80-
return outputMin
81-
}
74+
if (input <= inputMin) return outputMin
8275
return outputMax
8376
} // Input Range
8477

@@ -99,17 +92,13 @@ function interpolate(input, inputMin, inputMax, outputMin, outputMax, easing, ex
9992
} else {
10093
result = result * (outputMax - outputMin) + outputMin
10194
}
102-
10395
return result
10496
}
10597

10698
function colorToRgba(input) {
10799
var int32Color = normalizeColor(input)
108-
109100
if (int32Color === null) return input
110-
111101
int32Color = int32Color || 0 // $FlowIssue
112-
113102
var r = (int32Color & 0xff000000) >>> 24
114103
var g = (int32Color & 0x00ff0000) >>> 16
115104
var b = (int32Color & 0x0000ff00) >>> 8
@@ -128,7 +117,7 @@ var stringShapeRegex = /[0-9\.-]+/g
128117
* -45deg // values with units
129118
*/
130119
function createInterpolationFromStringOutputRange(config) {
131-
var outputRange = config.outputRange
120+
var outputRange = config.output
132121
outputRange = outputRange.map(colorToRgba)
133122

134123
// ->
@@ -147,16 +136,14 @@ function createInterpolationFromStringOutputRange(config) {
147136
/* $FlowFixMe(>=0.18.0): `value.match()` can return `null`. Need to guard
148137
* against this possibility.
149138
*/
150-
value.match(stringShapeRegex).forEach((number, i) => {
151-
outputRanges[i].push(+number)
152-
})
139+
value.match(stringShapeRegex).forEach((number, i) => outputRanges[i].push(+number))
153140
})
154141

155142
/* $FlowFixMe(>=0.18.0): `outputRange[0].match()` can return `null`. Need to
156143
* guard against this possibility.
157144
*/
158145
var interpolations = outputRange[0].match(stringShapeRegex).map((value, i) => {
159-
return Interpolation.create({ ...config, outputRange: outputRanges[i] })
146+
return Interpolation.create({ ...config, output: outputRanges[i] })
160147
})
161148

162149
// rgba requires that the r,g,b are integers.... so we want to round them, but we *dont* want to
@@ -176,4 +163,4 @@ function createInterpolationFromStringOutputRange(config) {
176163
function findRange(input, inputRange) {
177164
for (var i = 1; i < inputRange.length - 1; ++i) if (inputRange[i] >= input) break
178165
return i - 1
179-
}
166+
}

src/animated/SpringAnimation.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ export default class SpringAnimation extends Animation {
4040
}
4141

4242
if (this._initialVelocity !== undefined && this._initialVelocity !== null) this._lastVelocity = this._initialVelocity
43-
4443
if (this._delay) this._timeout = setTimeout(this.onUpdate, this._delay)
4544
else this.onUpdate()
4645
}

src/animated/createAnimatedComponent.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ export default function createAnimatedComponent(Component) {
3535
// need to re-render it. In this case, we have a fallback that uses
3636
// forceUpdate.
3737
var callback = () => {
38-
const didUpdate = ApplyAnimatedValues.current(this.refs['node'], this._propsAnimated.__getAnimatedValue(), this)
39-
if (didUpdate === false) this.forceUpdate()
38+
if (this.refs['node']) {
39+
const didUpdate = ApplyAnimatedValues.current(this.refs['node'], this._propsAnimated.__getAnimatedValue(), this)
40+
if (didUpdate === false) this.forceUpdate()
41+
}
4042
}
4143

4244
this._propsAnimated = new AnimatedProps(nextProps, callback)

0 commit comments

Comments
 (0)