All files scene.ts

100% Statements 27/27
50% Branches 1/2
100% Functions 18/18
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142      24x                                                     24x             10x                                   1x               25x   25x               3x   3x 3x     3x                     1x             7x               8x   4x             6x   3x               2x 4x 3x   2x               2x   1x      
import { Tweenable } from './tweenable'
 
export class Scene {
  private _tweenables: Tweenable[] = []
 
  /**
   * The {@link Scene} class provides a way to control groups of {@link
   * Tweenable}s. It is lightweight, minimalistic, and meant to provide
   * performant {@link Tweenable} batch control that users of Shifty
   * might otherwise have to implement themselves. It is **not** a robust
   * timeline solution, and it does **not** provide utilities for sophisticated
   * animation sequencing or orchestration. If that is what you need for your
   * project, consider using a more robust tool such as
   * [Rekapi](http://jeremyckahn.github.io/rekapi/doc/) (a timeline layer built
   * on top of Shifty).
   *
   * Please be aware that {@link Scene} does **not** perform any
   * automatic tween cleanup. If you want to remove a {@link Tweenable} from a
   * {@link Scene}, you must do so explicitly with either {@link Scene#remove}
   * or {@link Scene#empty}.
   *
   * <p class="codepen" data-height="677" data-theme-id="0" data-default-tab="js,result" data-user="jeremyckahn" data-slug-hash="qvZKbe" style="height: 677px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="Shifty Scene Demo">
   * <span>See the Pen <a href="https://codepen.io/jeremyckahn/pen/qvZKbe/">
   * Shifty Scene Demo</a> by Jeremy Kahn (<a href="https://codepen.io/jeremyckahn">@jeremyckahn</a>)
   * on <a href="https://codepen.io">CodePen</a>.</span>
   * </p>
   * <script async src="https://static.codepen.io/assets/embed/ei.js"></script>
   * @see https://codepen.io/jeremyckahn/pen/qvZKbe
   */
  constructor(...tweenables: Tweenable[]) {
    tweenables.forEach(this.add.bind(this))
  }
 
  /**
   * A copy of the internal {@link Tweenable}s array.
   */
  get tweenables() {
    return [...this._tweenables]
  }
 
  /**
   * The {@link !Promise}s for all {@link Tweenable}s in this {@link Scene} .
   * Note that each call of {@link Scene#tween} or {@link Scene#pause} creates
   * new {@link !Promise}s:
   *
   *     const scene = new Scene(new Tweenable());
   *     scene.play();
   *
   *     Promise.all(scene.promises).then(() =>
   *       // Plays the scene again upon completion, but a new promise is
   *       // created so this line only runs once.
   *       scene.play()
   *     );
   */
  get promises() {
    return this._tweenables.map(tweenable => tweenable.then())
  }
 
  /**
   * Add a {@link Tweenable} to be controlled by this {@link Scene}.
   * @return The {@link Tweenable} that was added.
   */
  add(tweenable: Tweenable) {
    this._tweenables.push(tweenable)
 
    return tweenable
  }
 
  /**
   * Remove a {@link Tweenable} that is controlled by this {@link Scene}.
   * @return The {@link Tweenable} that was removed.
   */
  remove(tweenable: Tweenable) {
    const index = this._tweenables.indexOf(tweenable)
 
    Eif (index > -1) {
      this._tweenables.splice(index, 1)
    }
 
    return tweenable
  }
 
  /**
   * {@link Scene#remove | Remove} all {@link Tweenable}s in this {@link
   * Scene}.
   * @return The {@link Tweenable}s that were removed.
   */
  empty() {
    // NOTE: This is a deliberate use of the tweenables getter here to create a
    // temporary array
    return this.tweenables.map(this.remove.bind(this))
  }
 
  /**
   * Whether or not any {@link Tweenable} in this {@link Scene} is playing.
   */
  get isPlaying() {
    return this._tweenables.some(({ isPlaying }) => isPlaying)
  }
 
  /**
   * Call {@link Tweenable#tween} on all {@link Tweenable}s in this {@link
   * Scene}.
   */
  tween() {
    this._tweenables.forEach(tweenable => tweenable.tween())
 
    return this
  }
 
  /**
   * Call {@link Tweenable#pause} all {@link Tweenable}s in this {@link Scene}.
   */
  pause() {
    this._tweenables.forEach(tweenable => tweenable.pause())
 
    return this
  }
 
  /**
   * Call {@link Tweenable#resume} on all paused {@link Tweenable}s in this
   * scene.
   */
  resume() {
    this._tweenables
      .filter(({ hasEnded }) => !hasEnded)
      .forEach(tweenable => tweenable.resume())
 
    return this
  }
 
  /**
   * Call {@link Tweenable#stop} on all {@link Tweenable}s in this {@link
   * Scene}.
   */
  stop(gotoEnd: boolean) {
    this._tweenables.forEach(tweenable => tweenable.stop(gotoEnd))
 
    return this
  }
}