Source: TiledSprite.js

TiledSprite.js


/**
 * 指定したスプライトをタイル状にリピード描画する。<br>
 * 背景スクロールなどに使用。
 * @see https://qiita.com/pentamania/items/ab736338d7b3b81b94cd
 * @class phina.display.TiledSprite
 * @memberOf phina.display
 * @extends phina.display.DisplayElement
 *
 * @example
 * // 背景をx軸スクロールする
 * const SCROLL_SPEED = 4;
 * const bgImage = phina.display.TiledSprite("background")
 *   .addChildTo(this)
 *   .on('enterframe', function() {
 *     this.offsetX -= SCROLL_SPEED;
 *   })
 * ;
 *
 * @param {String | phina.asset.Texture | phina.graphics.Canvas} image
 * @param {Number} [width]
 * @param {Number} [height]
 */
export default phina.createClass({
  superClass: phina.display.DisplayElement,

  init: function(image, width, height) {
    this.superInit();

    this._tileWidth = 0;
    this._tileHeight = 0;
    this._image = null;
    this._tempCanvas = phina.graphics.Canvas();
    this._canvasPattern = null;
    this.srcRect = phina.geom.Rect();
    this.offset = phina.geom.Vector2(0, 0);

    this.setImage(image, width, height);
  },

  /**
   * 画像をセット
   * @override
   * @instance
   * @memberOf phina.display.TiledSprite
   *
   * @param {String | phina.asset.Texture | phina.graphics.Canvas} image
   * @param {Number} [width]
   * @param {Number} [height]
   * @return {this}
   */
  setImage: function(image, width, height) {
    if (typeof image === 'string') {
      image = phina.asset.AssetManager.get('image', image);
    }
    this._image = image;
    this.width = this._image.domElement.width;
    this.height = this._image.domElement.height;

    if (width) { this.width = width; }
    if (height) { this.height = height; }
    this._tileWidth = this.width;
    this._tileHeight = this.height;

    this.frameIndex = 0; // run setFrameIndex

    return this;
  },

  /**
   * indexに応じてタイル化範囲をセット
   * @override
   * @instance
   * @memberOf phina.display.TiledSprite
   *
   * @param {Number} index
   * @param {Number} [width] タイル幅
   * @param {Number} [height] タイル高さ
   * @return {this}
   */
  setFrameIndex: function(index, width, height) {
    var tw  = width || this._tileWidth || this._width;
    var th  = height || this._tileHeight || this._height;
    var row = ~~(this.image.domElement.width / tw);
    var col = ~~(this.image.domElement.height / th);
    var maxIndex = row*col;
    index = index%maxIndex;

    var x = index%row;
    var y = ~~(index/row);
    this.srcRect.x = x*tw;
    this.srcRect.y = y*th;
    this.srcRect.width  = tw;
    this.srcRect.height = th;

    this._frameIndex = index;

    this._tileWidth = tw;
    this._tileHeight = th;
    this._renderPattern();

    return this;
  },

  /**
   * 原点オフセットを一括セット
   * @instance
   * @memberOf phina.display.TiledSprite
   *
   * @param {Number} x
   * @param {Number} y
   * @return {this}
   */
  setOffset: function(x, y) {
    this.offset.x = x;
    this.offset.y = y;
    return this;
  },

  /**
   * パターン元となるタイル画像を内部キャンバスに描画
   * @private
   * @instance
   * @memberOf phina.display.TiledSprite
   *
   * @return {void}
   */
  _renderPattern: function() {
    var tempCanvas = this._tempCanvas;
    var image = this.image.domElement;
    var srcRect = this.srcRect;
    tempCanvas.clear().setSize(srcRect.width, srcRect.height);
    tempCanvas.drawImage(image,
      srcRect.x, srcRect.y, srcRect.width, srcRect.height,
      0, 0, srcRect.width, srcRect.height
    );
    this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat');
  },

  /**
   * 描画処理。rendererによって毎フレーム実行される
   * @private
   * @override
   * @instance
   * @memberOf phina.display.TiledSprite
   *
   * @param {phina.graphics.Canvas} canvas rendererのcanvas
   * @return {void}
   */
  draw: function(canvas) {
    var context = canvas.context;
    context.fillStyle = this._canvasPattern;
    context.translate(
      -this._width * this.originX - this.offset.x,
      -this._height * this.originY - this.offset.y
    );
    context.fillRect(this.offset.x, this.offset.y, this._width, this._height);
  },

  _accessor: {

    /**
     * 画像テクスチャ
     * @instance
     * @memberOf phina.display.TiledSprite
     * @type {phina.asset.Texture | phina.graphics.Canvas}
     */
    image: {
      get: function() { return this._image; },
      set: function(v) { this.setImage(v); }
    },

    /**
     * テクスチャをフレーム分割していた場合のインデックス
     * @instance
     * @memberOf phina.display.TiledSprite
     * @type {number}
     */
    frameIndex: {
      get: function() { return this._frameIndex; },
      set: function(idx) { this.setFrameIndex(idx); }
    },

    /**
     * x軸原点オフセット
     * @instance
     * @memberOf phina.display.TiledSprite
     * @type {number}
     */
    offsetX: {
      get: function() { return this.offset.x; },
      set: function(v) { this.offset.x = v; }
    },

    /**
     * y軸原点オフセット
     * @instance
     * @memberOf phina.display.TiledSprite
     * @type {number}
     */
    offsetY: {
      get: function() { return this.offset.y; },
      set: function(v) { this.offset.y = v; }
    },
  },

});