Reputation: 8814
I have multiple animations running in parallel and I wondered if there are any benefits to having 3 Animated.Values over having a single Animated.Value and using interpolation.
Consider the following approach with multiple values:
const opacityValue = useRef(new Animated.Value(0)).current;
const rotationValue = useRef(new Animated.Value(0)).current;
const scaleValue = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.parallel(
Animated.timing(opacityValue, ...),
Animated.timing(rotationValue, ...),
Animated.timing(scaleValue, ...),
).start();
}, []);
const rotate = rotationValue.interpolate([]);
Consider this same approach with a single Animated.Value and interpolation included:
const opacityValue = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(opacityValue, ...).start();
}, []);
const rotationValue = opacityValue.interpolate([]);
const scaleValue = opacityValue.interpolate([]);
With the latter approach, only one animation is running on the native thread so could one assume that it is more performant?
What are the implications of using one over the other?
And is there any documentation that objectively describes in which situations one is better than the other?
Upvotes: 7
Views: 1448
Reputation: 4693
Disclaimer: I was interested in this and interested in checking out React Native, so I dived into React Native and its documentation and source code to see how it works and to understand you question. I haven't ever used React Native before stumbling upon this question. I do have decent background when it comes to interpolation, React and JS in general.
So first I thought that the .interpolate()
actually drives the animations and that the interpolate call actually interpolated the given value over time, but this is actually not true.
The animations, specifically the updates to some value over time are driven by the Animated.timing()
functions or the Animated.parallel
function, when start()
is called on them.
Animated.timing()
function takes an Animated.TimingAnimationConfig
parameter in where, for example, easing function of the animation is specified.
As for the .interpolate()
function, it is just like any interpolation function such as this:
// v0 = start value,
// v1 = end value,
// t = "progress" between 0 and 1 from v0 to t1
function lerp(v0, v1, t) {
return v0*(1-t)+v1*t;
}
But it is a bit more advanced than that as it can take multiple inputRanges
and outputRanges
, and supports various different interpolation (or easing) functions than just linear. More about it here.
But even so, it still acts as a function that just runs given numbers through some specified easing function and returns another number.
So back to your question, which I now understand better. What you are asking is whether it's better to have three parallel Animated.timing
functions running as an Animated.CompositeAnimation
than to have only one, "base" Animated.timing
function running and have the two other values interpolate their values from that.
Since Animated.timing
is something that actually, dynamically does something over time, there should probably be requestAnimationFrame usage somewhere (for web at least, but there must be something similar for native driver as well). And look at that, there is!
So this proves that Animated.timing
has to do with actual, real time repaints over time. But are there any obvious optimizations, if multiple animations run in parallel as as Animated.CompositeAnimation
? As far as I could find and look at the code, I would say no, the animations are simply put into an array and pretty much handled normally, so no advanced batching etc. that I could find:
But each animation will call requestAnimationFrame
separately. There is a discussion about the performance of multiple requestAnimationFrame
calls here on Stackoverflow. Bottom line being: Not much of a difference.
One more thing. Each animation has it's own onUpdate method and if we look here for example, we can see that that line is pretty much equivalent to performing easing/interpolation, so each animation does interpolation on every update to figure out what it's value should be for that update.
So to conclude all of this:
And therefore the final answer is:
No, there isn't any significant difference in performance. Animated.timing
objects most likely will use more memory than if you interpolate values on the fly. Neither the animations nor the interpolation function do any kind of memoization either.
As for the native driver, I don't have the brain to dig that deep, but you should probably use parallel timings in that case, as the animations are offloaded from JS (and the animations will most likely do their internal interpolation offloaded as well), but even so it probably doesn't make much of a difference.
If you really care about performance, you might get a decent increase by utilizing WebAssembly to implement the interpolation function and using that instead of what React Native supplies.
Here is my codesandbox that I used to test some things.
Upvotes: 2