Skip to content

Instantly share code, notes, and snippets.

@davej
Forked from simevidas/finished-polyfill.js
Last active August 29, 2019 14:14
Show Gist options
  • Save davej/23b0481016bcdb969df46423c6284771 to your computer and use it in GitHub Desktop.
Save davej/23b0481016bcdb969df46423c6284771 to your computer and use it in GitHub Desktop.
Animation.prototype.finished polyfill
// only polyfill .finished in browsers that already support animate()
if (document.body.animate) {
// Chrome does not seem to expose the Animation constructor globally
if (typeof Animation === 'undefined') {
window.Animation = document.body.animate({}).constructor;
}
if (!('finished' in Animation.prototype)) {
Object.defineProperty(Animation.prototype, 'finished', {
get() {
if (!this._finished) {
this._finished = this.playState === 'finished' ?
Promise.resolve() :
new Promise((resolve, reject) => {
this.addEventListener('finish', resolve, {once: true});
this.addEventListener('cancel', reject, {once: true});
});
}
return this._finished;
}
});
}
}
@davej
Copy link
Author

davej commented Jul 12, 2019

I was getting an error in Safari 13 (tech preview):

Unhandled Promise Rejection: TypeError: The Animation.finished getter can only be used on instances of Animation

This fork fixes that error.

image

@barneycarroll
Copy link

Hey @davej, thanks for this!

I tweaked it to use a WeakMap instead of modulos and expose the animation in promise settle states, may be of interest:

// only polyfill .finished in browsers that already support animate()
if (document.body.animate) {
  const finished = new WeakMap()

  // Chrome does not seem to expose the Animation constructor globally
  if (typeof Animation === 'undefined') {
    window.Animation = document.body.animate({}).constructor;
  }

  if (!('finished' in Animation.prototype)) {
    Object.defineProperty(Animation.prototype, 'finished', {
      get() {
        if (finished.has(this))
          return finished.get(this);

        const promise = this.playState === 'finished' ? 
          Promise.resolve() :
          new Promise((resolve, reject) => {
            this.addEventListener('finish', () => {
              resolve(this);
            }, {once: true});
            this.addEventListener('cancel', () => {
              reject(this);
            }, {once: true});
          });
        
        finished.set(this, promise);

        return promise;
      }
    });
  }
}

@davej
Copy link
Author

davej commented Aug 29, 2019

Thanks @barneycarroll, could you explain the advantages of this approach?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment