Animator performance optimizations
romelperez opened this issue · comments
The Animator
component used with the withAnimator
HOC has some performance issues when a large number of simultaneous animated components are rendered and updated. Since this requires many benchmark scenarios and deeply detailed analysis with many use-cases and side-effects probably not completely covered in the unit test cases, it is necessary to properly review the source code.
Test Analysis
The following analysis are made in development environment on Google Chrome with the DevTools opened. Production environment without DevTools are about 3x faster. But for testing purposes, the examples previews were recorded in development.
Without HTML animations
A simple component with no HTML animations rendered 1000 times:
performance1.mp4
const Test1Component = ({ animator }) => {
return <span>{animator.flow.value}</span>;
};
const animator1 = {
duration: { enter: 0, exit: 0 }
};
const Test1 = withAnimator(animator1)(Test1Component);
With HTML animations
A simple component with HTML animations using animejs rendered 1000 times:
performance2.mp4
const COLOR_ON = '#27efb5'; // cyan
const COLOR_OFF = '#efb527'; // orange
function useAnimateEntering (animator, element) {
anime({
targets: element.current,
easing: 'linear',
duration: animator.duration.enter,
backgroundColor: [COLOR_OFF, COLOR_ON]
});
}
function useAnimateExiting (animator, element) {
anime({
targets: element.current,
easing: 'linear',
duration: animator.duration.exit,
backgroundColor: [COLOR_ON, COLOR_OFF]
});
}
function Test2Component (props) {
const { animator, children } = props;
const element = React.useRef();
animator.setupAnimateRefs(element);
return (
<div
ref={element}
className='item'
>
{animator.flow.value}
</div>
);
}
const animator2 = {
duration: { enter: 200, exit: 200 },
useAnimateEntering,
useAnimateExiting
};
const Test2 = withAnimator(animator2)(Test2Component);
Possible Solutions
There are a few performance optimizations possible to make but with undesired side-effects or limitations. There may be others to be analyzed.
- Each
Animator
creates an utility instance to manage timed tasks calledmakeScheduler
usingsetTimeout
. Though it is recommended to userequestAnimationFrame
for improved performance, if there are enough simultaneous animated components being animated, all of them will block their animations until all of them are updated. This is not desired behavior and it is best all animations are rendered slower than not rendered at all. - The components may use JavaScript classes instead of functions since they are way faster when there are
React.useEffect
s in the components. But this limits some functionalities currently needed such as multipleReact.useContext
. - There could be used more
React.useMemo
for some functionalities but it may increase complexity without bringing a clear performance benefit.
References:
New animation API with the new packages @arwes/animator
and @arwes/animated
.
<Animator />
<Animated />