Source: animation-sequence.js

/**
 * @function animationSequence
 * @summary
 * Use this function to create a sprite animation sequence from a sprite sheet.
 * @description
 * <strong>Note: This function should only be called after the user has engaged with the ad as it will be loading additional assets.</strong>
 *
 * Options Object:
 * * eventLabel:
 *    * Type: string
 *    * Requirements: Required
 *    * Description: string prepended to event names
 *
 * * container:
 *    * Type: Movie Clip
 *    * Requirements: Required
 *    * Description: The movie clip that will contain your sprite animation sequence.
 *    * Notes: The sprite animation sequence will be scaled to fit the size of this container movie clip.
 *
 * * spritesheet:
 *    * Type: String
 *    * Requirements: Required
 *    * Description: The path to the spritesheet image.
 *
 * * json:
 *    * Type: String
 *    * Requirements: Optional
 *    * Description: The path to the JSON that accompanies your spritesheet image.
 *    * Note: Having JSON is required, however the property is optional. If no opts.json path is specified, the animationSequence function will automatically assume that you have created a JSON file in the src/json directory and have given it the same filename as your sprite sheet image.
 *
 * * framerate
 *    * Type: Integer
 *    * Requirements: Optional
 *    * Default: 24
 *    * Description: The framerate for the animation. Set this value to change how fast the animation sequence plays. Should be a non-zero integer (whole-number).
 *
 * * loop:
 *    * Type: Boolean
 *    * Requirements: Optional
 *    * Default: True
 *    * Description: If true, the animation sequence will play in a loop, restarting and playing thru each time it completes.
 *
 * * autoplay:
 *    * Type: Boolean
 *    * Default: True
 *    * Requirements: Optional
 *    * Description: If true, the animtion sequence will begin playing automatically, as soon as it is ready.
 *
 * Returns an object with a play and pause function:
 * ```
 * {
 *   play: function, (play or resume the animation)
 *   pause: function (pause the animation)
 * }
 * ```
 * The following event names are emitted:
 * ```
 *   eventLabel + '-ANIMATION_SEQUENCE_COMPLETE'
 *   eventLabel + '-ANIMATION_SEQUENCE_PROGRESS'
 *   eventLabel + '-ANIMATION_SEQUENCE_ERROR'
 *   eventLabel + '-ANIMATION_SEQUENCE_SPRITESHEET_ERROR'
 *   eventLabel + '-ANIMATION_SEQUENCE_JSON_ERROR'
 * ```
 * Sample Usage:
 * ```
 * var here = this
 * var animation = window.$b.snippets.animationSequence({
 *   eventLabel: 'KiaSedan',
 *   spritesheet: 'images/s.png',
 *   json: 'json/spritesheet.json',
 *   container: here.slateDesign.imageContainer,
 *   framerate: 48,
 *   autoplay: false,
 *   loop: true
 * })
 * var completeFunction = function (evt) {
 *   here.slateDesign.playBtn.addEventListener('click', function () {
 *     animation.play()
 *   })
 *   here.slateDesign.pauseBtn.addEventListener('click', function () {
 *     animation.pause()
 *   })
 * }
 * window.$b.on('KiaSedan-ANIMATION_SEQUENCE_COMPLETE, completeFunction)
 * ```
 *
 * @param {Object} opts Object containing all of the params to pass into the function
 * @param {string} opts.eventLabel a string prepended to the event names
 * @param {MovieClip} opts.container The movie clip that will contain your sprite animation sequence
 * @param {Object} opts.spritesheet
 * @param {string} opts.spritesheet.src the string path to the spritesheet image file (ex: 'images/mySpriteSheet.png')
 * @param {Object} opts.spritesheet.json JSON object containing size and coordinates for each image frame in sprite sheet
 * @param {number=} opts.framerate The framerate for the animation. Set this value to change how fast the animation sequence plays. Should be a non-zero integer (whole-number).
 * @param {boolean=} opts.loop Sets the animation sequence to play in a loop (default true)
 * @param {boolean=} opts.autoplay Sets the animation to automatically play as soon as it is ready (default true)
 * @param {string=} opts.root optional (external) baseUrl (if not specified in opts.spritesheet.src)
 * @returns {Object<play, pause>}
 *
 */

function animationSequence (opts) {
  var _this = {}
  // placeholders under images are loaded and sprite is created
  _this.play = function () {}
  _this.pause = function () {}
  var createjs = window.createjs

  if (!opts.eventLabel || typeof opts.eventLabel !== 'string') {
    console.error('You must provide a (string) label for the event prefix.')
    return _this
  }

  var eventLabel = opts.eventLabel
  var completeEvent = eventLabel + '-ANIMATION_SEQUENCE_COMPLETE'
  var errorEvent = eventLabel + '-ANIMATION_SEQUENCE_ERROR'
  var progressEvent = eventLabel + '-ANIMATION_SEQUENCE_PROGRESS'

  var message
  if (!(typeof opts === 'object')) {
    message = 'You must provide an options (opts) object with parameters to pass into the function'
    console.log(errorEvent, message)
    window.$b.emit(errorEvent, message)
    return _this
  }

  if (!(opts.container instanceof createjs.MovieClip)) {
    message = 'You must provide a container movie clip reference which will contain your spritesheet animation once it has been created.'
    console.log(errorEvent, message)
    window.$b.emit(errorEvent, message)
    return _this
  }

  if (!opts.spritesheet || typeof opts.spritesheet !== 'string') {
    message = 'You must provide a [string] path to an image file (opts.spritesheet)'
    console.log(errorEvent, message)
    window.$b.emit(errorEvent, message)
    return _this
  }

  if (opts.json && typeof opts.json !== 'string') {
    console.log('The json property must be a [string] path to a JSON file')
    return _this
  }

  // set loaded to false by default
  _this.loaded = false

  _this.spritesheet = opts.spritesheet

  var jsonFilename = opts.json || null

  // If no opts.json (json path) was specified, assume path to json folder + same filename as spritesheet image
  if (!opts.json) {
    var str = _this.spritesheet.split('/')
    str = str[str.length - 1].split('.')
    jsonFilename = 'json/' + str[0] + '.json'
  }

  // Turns to true if loading of JSON or spritesheet fails
  _this.failed = false

  _this.container = opts.container

  if (opts.loop !== false) {
    opts.loop = true
  }
  if (opts.autoplay !== false) {
    opts.autoplay = true
  }

  _this.loop = opts.loop
  _this.autoplay = opts.autoplay

  if (typeof opts.framerate === 'number') {
    if (opts.framerate < 1 || opts.framerate !== Math.floor(opts.framerate)) {
      if (opts.framerate < 1) {
        _this.framerate = 1
      } else {
        _this.framerate = Math.floor(opts.framerate)
      }
      console.warn('Framerate must be an integer greater than 0. It has been adjusted to the nearest non-zero whole number for you')
    } else {
      _this.framerate = opts.framerate
    }
  } else {
    _this.framerate = createjs.Ticker.framerate
  }

  // If there's a placeholder shape make it invisible right away.
  if (_this.container.shape) {
    _this.container.shape.visible = false
    _this.container.shape.alpha = 0
  }

  // Checks if blink is available. If it is, attach baseUrl
  var baseUrl = ''
  if (typeof window.$b === 'object') {
    baseUrl = window.$b.baseUrl
  }
  var root = opts.root === '' ? '' : opts.root || baseUrl || ''

  // Create simple image manifest to be loaded.
  _this.manifest = [{ src: root + _this.spritesheet }]

  // setupSprite Function: Sets up sprite after spritesheet is loaded and sprite is created
  // ============================================================================================================================
  _this.setupSprite = function () {
    // Resize sprite to match container size.
    _this.sprite.scaleX = _this.container.nominalBounds.width / _this.sprite.spriteSheet._frames[0].rect.width
    _this.sprite.scaleY = _this.container.nominalBounds.height / _this.sprite.spriteSheet._frames[0].rect.height

    // Add sprite to container
    _this.container.addChild(_this.sprite)

    // Set Framerate of Animation Sequence
    _this.sprite.framerate = _this.framerate

    _this.sprite.loop = _this.loop

    // Start Autoplay (if Set)
    if (_this.autoplay === true) {
      _this.sprite.gotoAndPlay(0)
    }

    // handle animationend
    // True: Do nothing (looping is the default behavior).
    // False: Stop the animation when it ends.
    _this.sprite.addEventListener('animationend', function () {
      if (_this.sprite.loop === false) {
        _this.sprite.gotoAndStop(_this.sprite.length - 1)
      }
    })

    _this.play = function () {
      var nextFrame = _this.sprite.currentFrame === _this.sprite.length - 1 ? 0 : _this.sprite.currentFrame + 1
      _this.sprite.gotoAndPlay(nextFrame)
    }
    _this.pause = function () {
      _this.sprite.stop()
    }
  }
  // ============================================================================================================================

  // setupSpritesheet Function: Once image has been loaded, create a sprite and start parsing json to generate individual frames.
  // ============================================================================================================================
  _this.setupSpritesheet = function () {
    var frames = []
    _this.json.frames.forEach(function (item, index) {
      var slot = item.frame
      var frame = [slot.x, slot.y, slot.w, slot.h]
      frames.push(frame)
    })

    var data = {
      images: [_this._loadedImage],
      frames: frames
    }

    var _spritesheet = new createjs.SpriteSheet(data)
    _this.sprite = new createjs.Sprite(_spritesheet, 0)
    _this.sprite.gotoAndStop(0)
    _this.sprite.length = frames.length
    _this.setupSprite()
  }
  // ============================================================================================================================

  // handleFileLoad Function
  // ============================================================================================================================
  _this.handleFileLoad = function (evt) {
    var item = evt.item
    var type = item.type
    if (type === createjs.LoadQueue.IMAGE) {
      _this._loadedImage = evt.result
    }
  }
  // ============================================================================================================================

  // handleComplete Function
  // ============================================================================================================================
  _this.handleComplete = function (evt) {
    if (_this.failed === false) {
      _this.setupSpritesheet()
      _this.loaded = true

      window.$b.emit(completeEvent, evt, _this)
    }
  }
  // ============================================================================================================================

  // handleProgress Function
  // ============================================================================================================================
  _this.handleProgress = function (evt) {
    window.$b.emit(progressEvent, evt)
  }
  // ============================================================================================================================

  // handleError Function
  // ============================================================================================================================
  _this.handleError = function (err) {
    _this.failed = true
    // This is called only if the error has to do with the spritesheet loading. JSON errors are handled in the Ajax.fail() function
    if (err.title) {
      var errorEvent = eventLabel + '-ANIMATION_SEQUENCE_SPRITESHEET_ERROR'
      console.log(errorEvent, err.title)
      try {
        window.$b.emit(errorEvent, err.title)
      } catch (err) {
        console.log(err)
      }
    }
  }
  // ============================================================================================================================

  // Begin Loading Sprite Sheet
  // ============================================================================================================================
  _this.load = function () {
    if (_this.loaded === false) {
      $.ajax({
        url: root + jsonFilename,
        crossDomain: true,
        dataType: 'json',
        timeout: 30000
      })
        .fail(function (err) {
          var errorEvent = eventLabel + '-ANIMATION_SEQUENCE_JSON_ERROR'
          console.log(errorEvent, err.statusText)
          try {
            window.$b.emit(errorEvent, err.statusText)
            _this.handleError(err)
          } catch (err) {
            console.log(err)
          }
        })
        .done(function (data) {
          _this.json = data
          _this.queue = new createjs.LoadQueue(true, root)
          _this.queue.loadManifest(_this.manifest)
          _this.queue.on('error', _this.handleError, this)
          _this.queue.on('fileload', _this.handleFileLoad, this)
          _this.queue.on('complete', _this.handleComplete, this)
          _this.queue.on('progress', _this.handleProgress, this)
        })
    }
  }
  // ============================================================================================================================

  return _this
} // END - function createGifPlayer(opts)

window.$b = window.$b || {}
window.$b.snippets = window.$b.snippets || {}
var snippets = window.$b.snippets
snippets.animationSequence = animationSequence