jeremyckahn / shifty

The fastest TypeScript animation engine on the web
https://jeremyckahn.github.io/shifty/doc/
MIT License
1.54k stars 88 forks source link

Scene API #110

Closed jeremyckahn closed 5 years ago

jeremyckahn commented 5 years ago

Per #109, Shifty would benefit from a "Scene" API. The use case is to have multiple sets of tweens managed independently. Here's how it might work:

import { Tweenable, Scene } from 'shifty';

const sceneA = new Scene(
  // Tweenable 1
  new Tweenable( ... ),
  // Tweenable 2
  new Tweenable( ... )
);

const sceneB = new Scene(
  // Tweenable 3
  new Tweenable( ... ),
  // Tweenable 4
  new Tweenable( ... )
);

// Plays Tweenables 1 and 2 but not 3 and 4
sceneA.play();

This Scene object should be lightweight and focus solely on being a convenience utility to manage sets of tweens. It should not be concerned with providing a robust set of features to manage tween sequencing or granular timeline management, as Rekapi already solves that problem pretty comprehensively. This should be consistent with Shifty's focus on remaining lightweight and optimized for performance.

To start with, a good constraint to adhere to is to simply provide batch versions of Tweenable's playback APIs: pause, resume, and stop. Other analogous Tweenable methods might make sense here, but I'd prefer to wait for a later iteration as their use cases aren't quite as clear right now. So, I'm thinking that the API would have this shape:

export class Scene {
  /**
   * @param {...Tweenable} tweenables
   */
  constructor(...tweenables) {}

  /**
   * @return {Array.<Tweenable>} A copy of the internal Tweenables array.
   */
  get tweenables() {}

  /**
   * @param {Tweenable} tweenable
   * @return {Tweenable}
   */
  add(tweenable) {}

  /**
   * @param {Tweenable} tweenable
   * @return {Tweenable}
   */
  remove(tweenable) {}

  /**
   * @return {boolean}
   */
  isPlaying() {}

  /**
   * @return {Scene}
   */
  play() {}

  /**
   * @return {Scene}
   */
  pause() {}

  /**
   * @return {Scene}
   */
  resume() {}

  /**
   * @param {boolean} [gotoEnd]
   * @return {Scene}
   */
  stop(gotoEnd) {}
}

Note: This issue is a draft and this initial comment will be updated to reflect the latest state of this feature's development.

jeremyckahn commented 5 years ago

One limitation that comes to mind is that none of these methods will be able to return a Promise like tween or resume do. The reason for this is that addTweenable and removeTweenable may be called while the scene is playing and that would break the Promise that would be returned from an earlier call to Scene's play and resume methods.

jeremyckahn commented 5 years ago

I'm planning to start implementing this pretty soon, but before I do I'm going to modernize the toolchain a bit as Shifty's devDependencies are pretty out of date. I'm also probably going to switch the test harness to Jest.

4ian commented 5 years ago

Make sense to me 👍No concerns over the API as far as I can see.

jeremyckahn commented 5 years ago

I just published 2.7.4-alpha.0 which is a stable WIP implementation of the Scene API. I ended up removing a few APIs that I felt would result in confusing behavior (like seek). It can be tried out in this CodePen.

I still need to document this and add some explanation around what Scene is and isn't good for (it's intentionally minimalistic). I'd love to get some feedback on this to see what people think so far!

jeremyckahn commented 5 years ago

After playing with this a bit more, I think batched Tweenable#tween and Tweenable#resume operations need to be separate, so I brought back Scene#resume in 2.7.4-alpha.1.

jeremyckahn commented 5 years ago

I just published 2.7.4-alpha.2, which includes a fix for re-tweening paused tweens. I updated the CodePen to demo more of the Scene playback APIs.

4ian commented 5 years ago

Sweet! I'll give it a try in GDevelop tween extension asap, probably tomorrow evening :)