From 7baecca9a4442a5a643c6f337ee10ac48450fc88 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Mon, 13 Feb 2017 15:30:28 -0800 Subject: [PATCH] Native Animated blog post Summary: Blog post I promised about native animated :) There's a few placeholders, one could be for a little graphic that to represent the node graph (would appreciate if someone with good design skills would make one for me hehe) and the other one for other resources about native Animated if you know of any other. Closes https://github.com/facebook/react-native/pull/12202 Differential Revision: D4554402 Pulled By: hramos fbshipit-source-id: db56d841c5c21cd9b7af00d020d576cd243b2778 --- ...-02-14-using-native-driver-for-animated.md | 156 ++++++++++++++++++ blog/img/animated-diagram.png | Bin 0 -> 23808 bytes 2 files changed, 156 insertions(+) create mode 100644 blog/2017-02-14-using-native-driver-for-animated.md create mode 100644 blog/img/animated-diagram.png diff --git a/blog/2017-02-14-using-native-driver-for-animated.md b/blog/2017-02-14-using-native-driver-for-animated.md new file mode 100644 index 000000000..43c4e6e9a --- /dev/null +++ b/blog/2017-02-14-using-native-driver-for-animated.md @@ -0,0 +1,156 @@ +--- +title: Using Native Driver for Animated +author: Janic Duplessis +authorTitle: Software Engineer at App & Flow +authorURL: https://twitter.com/janicduplessis +authorImage: https://secure.gravatar.com/avatar/8d6b6c0f5b228b0a8566a69de448b9dd?s=128 +authorTwitter: janicduplessis +category: engineering +--- + +For the past year, we've been working on improving performance of animations that use the Animated library. Animations are very important to create a beautiful user experience but can also be hard to do right. We want to make it easy for developers to create performant animations without having to worry about some of their code causing it to lag. + +## What is this? + +The Animated API was designed with a very important constraint in mind, it is serializable. This means we can send everything about the animation to native before it has even started and allows native code to perform the animation on the UI thread without having to go through the bridge on every frame. It is very useful because once the animation has started, the JS thread can be blocked and the animation will still run smoothly. In practice this can happen a lot because user code runs on the JS thread and React renders can also lock JS for a long time. + +## A bit of history... + +This project started about a year ago, when Exponent built the li.st app on Android. [Krzysztof Magiera](https://twitter.com/kzzzf) was contracted to build the initial implementation on Android. It ended up working well and li.st was the first app to ship with native driven animations using Animated. A few months later, [Brandon Withrow](https://github.com/buba447) built the initial implementation on iOS. After that, [Ryan Gomba](https://twitter.com/ryangomba) and myself worked on adding missing features like support for `Animated.event` as well as squash bugs we found when using it in production apps. This was truly a community effort and I would like to thanks everyone that was involved as well as Exponent for sponsoring a large part of the development. It is now used by `Touchable` components in React Native as well as for navigation animations in the newly released [React Navigation](https://github.com/react-community/react-navigation) library. + +## How does it work? + +First, let's check out how animations currently work using Animated with the JS driver. When using Animated, you declare a graph of nodes that represent the animations that you want to perform, and then use a driver to update an Animated value using a predefined curve. You may also update an Animated value by connecting it to an event of a `View` using `Animated.event`. + +![](/react-native/blog/img/animated-diagram.png) + +Here's a breakdown of the steps for an animation and where it happens: +- JS: The animation driver uses `requestAnimationFrame` to execute on every frame and update the value it drives using the new value it calculates based on the animation curve. +- JS: Intermediate values are calculated and passed to a props node that is attached to a `View`. +- JS: The `View` is updated using `setNativeProps`. +- JS to Native bridge. +- Native: The `UIView` or `android.View` is updated. + +As you can see, most of the work happens on the JS thread. If it is blocked the animation will skip frames. It also needs to go through the JS to Native bridge on every frame to update native views. + +What the native driver does is move all of these steps to native. Since Animated produces a graph of animated nodes, it can be serialized and sent to native only once when the animation starts, eliminating the need to callback into the JS thread; the native code can take care of updating the views directly on the UI thread on every frame. + +Here's an example of how we can serialize an animated value and an interpolation node (not the exact implementation, just an example). + +Create the native value node, this is the value that will be animated: +``` +NativeAnimatedModule.createNode({ + id: 1, + type: 'value', + initialValue: 0, +}); +``` + +Create the native interpolation node, this tells the native driver how to interpolate a value: +``` +NativeAnimatedModule.createNode({ + id: 2, + type: 'interpolation', + inputRange: [0, 10], + outputRange: [10, 0], + extrapolate: 'clamp', +}); +``` + +Create the native props node, this tells the native driver which prop on the view it is attached to: +``` +NativeAnimatedModule.createNode({ + id: 3, + type: 'props', + properties: ['style.opacity'], +}); +``` + +Connect nodes together: +``` +NativeAnimatedModule.connectNodes(1, 2); +NativeAnimatedModule.connectNodes(2, 3); +``` + +Connect the props node to a view: +``` +NativeAnimatedModule.connectToView(3, ReactNative.findNodeHandle(viewRef)); +``` + +With that, the native animated module has all the info it needs to update the native views directly without having to go to JS to calculate any value. + +All there is left to do is actually start the animation by specifying what type of animation curve we want and what animated value to update. Timing animations can also be simplified by calculating every frame of the animation in advance in JS to make the native implementation smaller. +``` +NativeAnimatedModule.startAnimation({ + type: 'timing', + frames: [0, 0.1, 0.2, 0.4, 0.65, ...], + animatedValueId: 1, +}); +``` + +And now here's the breakdown of what happens when the animation runs: +- Native: The native animation driver uses `CADisplayLink` or `android.view.Choreographer` to execute on every frame and update the value it drives using the new value it calculates based on the animation curve. +- Native: Intermediate values are calculated and passed to a props node that is attached to a native view. +- Native: The `UIView` or `android.View` is updated. + +As you can see, no more JS thread and no more bridge which means faster animations! 🎉🎉 + +## How do I use this in my app? + +For normal animations the answer is simple, just add `useNativeDriver: true` to the animation config when starting it. + +Before: +``` +Animated.timing(this.state.animatedValue, { + toValue: 1, + duration: 500, +}).start(); +``` +After: +``` +Animated.timing(this.state.animatedValue, { + toValue: 1, + duration: 500, + useNativeDriver: true, // <-- Add this +}).start(); +``` + +Animated values are only compatible with one driver so if you use native driver when starting an animation on a value, make sure every animation on that value also uses the native driver. + +It also works with `Animated.event`, this is very useful if you have an animation that must follow the scroll position because without the native driver it will always run a frame behind of the gesture because of the async nature of React Native. + +Before: +``` + + {content} + +``` +After: +``` + + {content} + +``` + +## Caveats + +Not everything you can do with Animated is currently supported in Native Animated. The main limitation is that you can only animate non-layout properties, things like `transform`, `opacity` and `backgroundColor` will work but flexbox and position properties won't. Another one is with `Animated.event`, it will only work with direct events and not bubbling events. This means it does not work with `PanResponder` but does work with things like `ScrollView#onScroll`. + +Native Animated has also been part of React Native for quite a while but has never been documented because it was considered experimental. Because of that make sure you are using a recent version (0.40+) of React Native if you want to use this feature. + +## Resources + +For more information about animated I recommend watching [this talk](https://www.youtube.com/watch?v=xtqUJVqpKNo) by [Christopher Chedeau](https://twitter.com/Vjeux). + +If you want a deep dive into animations and how offloading them to native can improve user experience there is also [this talk](https://www.youtube.com/watch?v=qgSMjYWqBk4) by [Krzysztof Magiera](https://twitter.com/kzzzf). diff --git a/blog/img/animated-diagram.png b/blog/img/animated-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..26019db8ac8d249b7e973a70496127b47aa4e96a GIT binary patch literal 23808 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 +t8EtfE})Rt;M=ni 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