arwes / arwes

Futuristic Sci-Fi UI Web Framework.

Home Page:https://arwes.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 called makeScheduler using setTimeout. Though it is recommended to use requestAnimationFrame 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.useEffects in the components. But this limits some functionalities currently needed such as multiple React.useContext.
  • There could be used more React.useMemo for some functionalities but it may increase complexity without bringing a clear performance benefit.

References: