flackr / scroll-timeline

A polyfill of ScrollTimeline.
Apache License 2.0
951 stars 94 forks source link

How to dispose the animation after no longer needed? #196

Open MurhafSousli opened 9 months ago

MurhafSousli commented 9 months ago

I have an option where I need to re-create the animation instance, how can I dispose the animation before creating a new one?

constructor() {
  if (!CSS.supports('animation-timeline', 'scroll()')) {

    if (this.cmp.direction() === 'rtl') {
      this.animation = startPolyfill(this.nativeElement, this.cmp.viewport.nativeElement, this.polyfillAxis, this.polyfillKeyframe);
    } else {
      this.animation = startPolyfill(this.nativeElement, this.cmp.viewport.nativeElement, this.polyfillAxis, this.polyfillKeyframe);
    }
  }
}

function startPolyfill(element: HTMLElement, source: HTMLElement, axis: 'x' | 'y', keyframeProperty: string): Animation {
  return element.animate(
    {
      [keyframeProperty]: [
        'var(--_scrollbar-thumb-transform-from)',
        'var(--_scrollbar-thumb-transform-to)'
      ]
    },
    {
      fill: 'both',
      easing: 'linear',
      timeline: new ScrollTimeline({ source, axis })
    } as any
  );
}
kevers-google commented 9 months ago

The animation API has a cancel method. In your example, the "if" and "else" blocks look the same. Should the blocks be setting different keyframe options? If so, then rather than cancelling and restarting the animation, calling animation.effect.setKeyframes() is an option.

MurhafSousli commented 9 months ago

@kevers-google The parameters are the same, however, the only change is in the CSS variable --_scrollbar-thumb-transform-from and --_scrollbar-thumb-transform-to which is done in CSS. When direction is LTR, I use translate3d but in RTL I have to use right because translate3d in RTL mode goes to the opposite direction in a weird way, even if I invert the position to fix the animation direction, the actual DOM element still goes in the opposite direction making the hover and click events decoupled from the animated element location.

All I need is to re-initialize the animation when the direction is changed.

So the cancel function clean up the animation right?

kevers-google commented 8 months ago

Yes, animation.cancel() puts the animation into an idle state where it is no longer active. The animation will eventually be garbage collected provided there are no live references in JavaScript.

Canceling and then recreating the animation will work, but could result in flicker in some cases since the animation is momentarily not in effect. You can start the new animation before cancelling the old one to avoid flicker since composite ordering will ensure that the new animation replaces the old one. The reason for cancelling the old animation in this case, is performance and memory (avoid stale animations piling up).

An alternate approach is

requestAnimationFrame(() => { // Apply CSS change requestAnamationFrame(() => { animation.effect.setKeyframes(...); }); });

In other words, you can replace the keyframes on the existing animation rather than creating a new animation.

Long story short, you have options. If cancelling and creating a new animation works well enough for your application, then that might be simplest. If you encounter flicker problems when restarting the animation, then you can consider one of the other options.