Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v9 canary #808

Closed
wants to merge 738 commits into from
Closed

v9 canary #808

wants to merge 738 commits into from

Conversation

aleclarson
Copy link
Contributor

@aleclarson aleclarson commented Sep 9, 2019

(Forked from #797)

Status: Basically done 🥳

Try the latest canary here.

Todos

  • The remaining useTransition todos here

  • Make Controller#reset work with async to prop

  • Add pause prop to Controller (which powers useSpring etc)

  • Add DelayQueue for pauseable delays

  • Demo for config.frequency/damping vs config.tension/friction

  • Write tests for:

    • SpringValue#pause method

    • Interpolation object as to prop

    • nested to function props


Details

First, an example demonstrating one of the main inspirations for this PR:

const MyView = () => {
 	// Nothing new here.
  	const {width, height} = useSpring({ width: 10, height: 10 })
  	return (
		<a.div 
			style={{ width, height, backgroundColor: 'red' }} 
			onClick={async () => {
 				// Update the animation of each value separately (no refs required)!
				await width.start(width.get() + 10)
				// Pass other props as the 2nd argument
				await height.start(height.get() + 10, {delay: 1000})
				// Async "to" props are supported for single values
				await width.start(async (update, stop) => {
					await update(100)
					await update(0)
				})
			}}
		/>
	)
}

But wait, there's more!

New features 🎉

  • Added the SpringValue class, which controls the animation of one value or an array of values.

    • The FrameLoop now deals with SpringValue objects instead of Controller objects.
    • Every SpringValue object has a queue property that stores anything you pass to the update method. When ready, you call the start method to flush the queue, which may or may not trigger an animation (depending on which props were given).
    // The queue-based methods are just like the `Controller` class
    const {width} = useSpring({ width: 0 })
    useEffect(() => {
      // Animate width from 0 to 100, then back to 0 one second later.
      width.update({ to: 100 }).update({ to: 0, delay: 1000 }).start()
    })
  • Every prop of an animated component now supports any FluidValue) object as its value. This allows any observable library to integrate with react-spring. In fact, the SpringValue class extends the FluidValue class. 🔥

  • The to prop also allows passing any FluidValue, and that spring will animate towards the FluidValue on every frame.

    • If the immediate prop is true, that spring will stick to the FluidValue instead of animating to it.
    • The from prop also allows this, but it only recaches the FluidValue's raw value when first animated and when the reset prop is true.
// Make the `height` animate reactively towards half the `width`
const {width} = useSpring({ width: 0 })
const {height} = useSpring({ 
	height: width.to(width => width / 2)
})
  • Async to props (eg: an array or async function) can now be nested without cancelling each other:
// Useless example of nested async `to` props
const {width} = useSpring({
	from: {width: 0},
	to: async next => {
		await next({
			to: async next => {
				await next({
					to: [{ width: 100 }, { width: 0 }]
				})
			}
		})
	}
})
  • The onChange prop (passed to useSpring for example) lets you observe changes to all of your values (one animated value per call).
useSpring({
  width: props.width,
  height: props.height,
  onChange: (value, spring) => {
    console.log(spring.key, '=>', value)
  }
})
  • The SpringValue#finish method lets you skip to the end of an animation, which calls any onRest callbacks.
const {width} = useSpring({ width: 10, from: { width: 0 } })
width.finish()
  • The AnimatedInterpolation class was replaced by the Interpolation class. It caches its computed value, instead of recomputing it on every get call.
const {width} = useSpring({ width: 10 })
const height = width.to(width => width / 2)
height.get() // 5
  • The config.bounce prop lets you create a bouncing animation using a spring. You specify this alongside tension, friction, etc
useSpring({
  width: props.width,
  // Reduce velocity by 50% on every bounce
  config: { bounce: 0.5 },
})
  • The config.frequency prop (aka: the "natural frequency") is a new alternative to config.tension that defines how frequent the spring bounces in a frictionless environment.

  • The config.damping prop (aka: the "damping ratio") can only be used with config.frequency defined. The config.damping prop defaults to 1 when undefined, which means the spring will never bounce. Set this value between 0 and 1 to control the spring's bounciness.

  • The config.round prop rounds the animated value to the nearest multiple of the given number.

// Animate "x" toward "props.x" but stick to the pixel grid along the way.
useSpring({
  x: props.x, 
  config: { round: 1 / window.devicePixelRatio },
})
  • The immediate, reset, and cancel props can each equal a boolean, a function, an array of keys, or a single key.

Breaking changes ☠️

  • The useTransition hook has been rewritten from the ground up. More info at feat: useTransition rewrite #809

  • The Controller#destroy method is now called dispose, and the SpringValue class has a method with the same name. Note: Only call the dispose method on objects you've created manually (using new syntax).

  • The Controller#reset method now works like { reset: true } passed to useSpring

  • The Controller#stop method now takes a string or an array of strings. No more variable arguments.

const [{ foo }, set, stop] = useSpring(() => props)

stop('foo')
stop(['foo', 'bar'])
foo.stop()
  • Event props like onChange and onRest only affect a single animation, which means you must define them whenever you update the to prop. Alternatively, you can define them on the defaultProps object property by passing the default: true prop. Note: Event props provided in useSpring(() => props) will be treated as "default props" (which means they'll be used when an animation has no handler of its own).

  • The diffing algorithm has been moved (from the Controller#_diff and Controller#_animate methods) to the SpringValue#_update method. Take a peek and you'll see a new approach to diffing, where the props are applied to the animation property on an individual basis, instead of the old approach, where every prop was cached in an internal props property before updating the animation object. This new approach gives us more control over the edge cases of various props, but some props may behave differently (in rare use cases only).

    • Currently, only 2 props have timestamps (from and to).

Bug fixes 🐛

  • Fixed a small bug with animated function components that don't forward their ref prop. They should be re-rendered on every animation frame now, as expected.

Other changes 🚧

  • The Animated nodes no longer form a graph of dependencies. Instead, the SpringValue objects are passed as props to animated components, and the AnimatedProps object tracks which SpringValue objects exist in its props at any given time. This removes the need for an Animated#updatePayload method and the moveChildren helper once used by the Controller class.

  • Animated components now cache any FluidValue objects found in their props (unless they're nested in an object other than the top-level props, an animated style, an animated transform, or an animated array).

  • The FrameLoop now updates its springs in topological order, based on which springs depend on the values of other springs.

  • Erased the concept of "batched frame updates" from the FrameLoop class, which is only useful in the case of native-driven springs (which we don't have yet).

@aleclarson aleclarson added this to the v9.0.0 milestone Sep 9, 2019
@aleclarson aleclarson requested review from drcmda and dbismut September 9, 2019 17:31
@aleclarson aleclarson mentioned this pull request Sep 9, 2019
@aleclarson aleclarson force-pushed the feat/spring-class branch 3 times, most recently from 21ffd77 to 921c10f Compare September 9, 2019 18:14
@PascalSenn
Copy link

SpringValue makes the API much simpler! I ❤ it!

@PascalSenn
Copy link

Status: Basically done 🥳

🤘
🤘
🤘
🤘

This ensures the next "start" call will start animating even if the "to" value is identical to the previous "to" value (before "set" was called)
When the "to" value is fluid, the goal value can change at any time before "onRest" is called. So we need to compute the goal value from within onRest. Otherwise, the "finished" property (on the animation result) might not be accurate.
instead of an abstract value. This keeps the compiler from initializing `idle` to undefined in the constructor, which was triggering an error because SpringValue#idle is a getter w/o a setter.
But noop updates created by the "loop" prop are still never repeated.
This only affects SpringValue objects owned by a Controller object.
@aleclarson aleclarson force-pushed the feat/spring-class branch from a221046 to 60041ab Compare May 2, 2020 22:50
aleclarson added 3 commits May 2, 2020 19:26
FrameLoop is incompatible with r3f's render loop, because of how the FrameLoop calls r3f's `invalidate` function inside r3f's global effects queue. In doing that, the `invalidate` call is ignored, and the frameloop is effectively paused.
@aleclarson aleclarson force-pushed the feat/spring-class branch from 499eeea to efd9253 Compare May 3, 2020 19:13
@aleclarson
Copy link
Contributor Author

Closed in favor of #978

@aleclarson aleclarson closed this May 3, 2020
aleclarson added a commit that referenced this pull request May 3, 2020
aleclarson added a commit that referenced this pull request May 3, 2020
aleclarson added a commit that referenced this pull request May 3, 2020
@aleclarson aleclarson deleted the feat/spring-class branch February 23, 2021 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants