Source: core/Sprite.js

import {Container} from './Container';
import {MovieClip} from '../animation/MovieClip';
import {Rectangle} from '../math/Rectangle';

/**
 * 位图精灵图,继承至Container
 *
 * ```js
 * var loadBox = JC.loaderUtil({
 *    frames: './images/frames.png'
 * });
 * var sprite = new JC.Sprite({
 *      texture: loadBox.getById('frames'),
 *      frame: new JC.Rectangle(0, 0, w, h),
 *      width: 100,
 *      height: 100,
 *      animations: {
 *          fall: {start: 0,end: 4,next: 'stand'},
 *          fly: {start: 5,end: 9,next: {movie: 'stand', repeats: 2}},
 *          stand: {start: 10,end: 39},
 *          walk: {start: 40,end: 59,next: 'stand'},
 *          other: [ 0, 1, 2, 1, 3, 4 ], // 同样接受数组形势
 *      }
 * });
 * ```
 *
 * @class
 * @memberof JC
 * @extends JC.Container
 * @param {Object} options
 * @param {JC.Texture} options.texture 图片纹理
 * @param {JC.Rectangle} [options.frame] 当是逐帧或者是裁切显示时需要配置,显示的矩形区域
 * @param {Number} [options.width] 实际显示的宽,可能会缩放图像
 * @param {Number} [options.height] 实际显示的高,可能会缩放图像
 * @param {Object} [options.animations] 逐帧的预置帧动画配置
 */
function Sprite(options = {}) {
  Container.call(this);

  this._width = 0;

  this._height = 0;

  this.ready = false;

  this.upTexture(options);

  this.movieClip = new MovieClip(this, options);
}
Sprite.prototype = Object.create(Container.prototype);

/**
 * 更新纹理对象
 *
 * @private
 * @param {json} options
 */
Sprite.prototype.upTexture = function(options) {
  if (!options.texture) return;
  if (!options.texture.loaded) {
    this.ready = false;
    options.texture.on('load', () => {
      this.upTexture(options);
      this.ready = true;
    });
    return;
  }

  this.ready = true;
  this.texture = options.texture;
  this.naturalWidth = options.texture.naturalWidth;
  this.naturalHeight = options.texture.naturalHeight;
  this.frame = options.frame ||
  new Rectangle(
    0,
    0,
    this.naturalWidth,
    this.naturalHeight
  );

  this.width = options.width || this.frame.width;
  this.height = options.height || this.frame.height;
  if (this.movieClip) {
    this.movieClip.preFrame = null;
  }
};

/**
 * 当前图片对象的width
 *
 * @name width
 * @member {Number}
 * @memberof JC.Sprite#
 */
Object.defineProperty(Sprite.prototype, 'width', {
  get: function() {
    return this._width;
  },
  set: function(width) {
    if (this._width !== width) {
      this._width = width;
      this.updateGeometry();
    }
  },
});

/**
 * 当前图片对象的height
 *
 * @name height
 * @member {Number}
 * @memberof JC.Sprite#
 */
Object.defineProperty(Sprite.prototype, 'height', {
  get: function() {
    return this._height;
  },
  set: function(height) {
    if (this._height !== height) {
      this._height = height;
      this.updateGeometry();
    }
  },
});

/**
 * 更新对象的事件几何形态
 * note: 此处的setArea是懒更新,如果需要
 *
 * @private
 */
Sprite.prototype.updateGeometry = function() {
  const rect = new Rectangle(0, 0, this.width, this.height);
  this._bounds.clear().addRect(rect);
  this.setArea(rect);
};

/**
 * 更新对象的动画姿态
 *
 * @private
 * @param {number} snippet
 */
Sprite.prototype.updateAnimation = function(snippet) {
  this.animation.update(snippet);
  this.movieClip.update(snippet);
};

/**
 * 播放逐帧动画
 * @param {Object} options 可以是播放配置对象
 * @param {String|Array} options.movie 预置的动画名,或者是帧索引数组
 * @param {Number} [options.fillMode] 结束时停留在哪一帧
 * @param {Boolean} [options.repeats] 重复播放次数
 * @param {Boolean} [options.infinite] 无限循环,优先级比 repeats 高
 * @param {Boolean} [options.alternate] 循环时交替播放
 * @param {Number} [options.fps] 当前动画将使用的帧率
 * @return {MovieClip}
 */
Sprite.prototype.playMovie = function(options) {
  return this.movieClip.playMovie(options);
};

/**
 * 更新对象本身的矩阵姿态以及透明度
 *
 * @private
 * @param {context} ctx
 */
Sprite.prototype.renderMe = function(ctx) {
  if (!this.ready) return;
  const frame = this.movieClip.getFrame();
  ctx.drawImage(
    this.texture.texture,
    frame.x,
    frame.y,
    frame.width,
    frame.height,
    0,
    0,
    this.width,
    this.height
  );
};

export {Sprite};