Source: ScrollableLabelArea.js

ScrollableLabelArea.js

/* global phina:false */

// TODO: 最大スクロール可能位置をキャッシュ・設定する方法
var MIN_VELOCITY = 0.1;

/**
 * スクロール可能なLabelAreaクラス
 * @class phina.ui.ScrollableLabelArea
 * @memberOf phina.ui
 * @extends phina.ui.LabelArea
 *
 * @example
 * ScrollableLabelArea({
 *   text: "The quick brown fox jumps over the lazy dog.",
 *   width: 200,
 *   scrollFriction: 2,
 * })
 * .addChildTo(this);
 *
 * @param {object} [options] - The optional setup parameters. phina.ui.LabelAreaパラメータも有効です。
 * @param {number} [options.scrollFriction=0.86] - ドラッグスクロール時の摩擦係数を設定します。
 * @param {number} [options.scrollSpeed=3] - スクロールの感度・速度を指定します。
 * @param {number} [options.autoScroll=false] - 自動スクロールします。
 * @param {number} [options.limitScroll=true] - スクロール下限制限を有効にします。
 */
export default phina.createClass({
  superClass: phina.ui.LabelArea,

  init: function(options) {
    options = ({}).$extend({
      scrollFriction: 0.86,
      scrollSpeed: 3,
      autoScroll: false,
      limitScroll: true,
    }, options);
    this.superInit(options);
    this._scrollFriction = options.scrollFriction;
    this._scrollSpeed = options.scrollSpeed;
    this._limitScroll = options.limitScroll;

    this._setupScroll(options.autoScroll);
  },

  /**
   * @private
   * @instance
   * @memberof phina.ui.ScrollableLabelArea
   *
   * @param {boolean} autoScroll - [description]
   * @return {void} - [description]
   */
  _setupScroll: function(autoScroll) {
    var self = this;
    var velocity = 0;

    if (!autoScroll) {
      /* user drag scroll */
      this.setInteractive(true);
      this.on('pointstart', (e) => {
        velocity = 0;
      });
      this.on('pointmove', (e) => {
        velocity = e.pointer.dy * this._scrollSpeed;
      });
      this.on('enterframe', () => {
        if (velocity !== 0) {
          velocity *= this._scrollFriction;
          var maxScrollableY = Math.max(0, this.textAreaHeight + this.padding - this.canvasHeight);
          var nextY = this.scrollY - velocity;
          this.scrollY = (!this._limitScroll) ? nextY : Math.clamp(nextY, 0, maxScrollableY);
          velocity = (velocity.abs() < MIN_VELOCITY) ? 0 : velocity;
        }
      });

      // this.on('pointend', function(e) {
      //   physical.force(0, lastForce + lastMove);
      // });
      // this.on('pointmove', (e)=> {
      //   var maxScrollableY = Math.max(0, this.textAreaHeight - this.canvasHeight + this.padding);
      //   this.scrollY = Math.clamp(this.scrollY - e.pointer.dy, 0, maxScrollableY);
      // });
    } else {
      /* auto-scroll */
      this.on('enterframe', () => {
        var maxScrollableY = Math.max(0, this.textAreaHeight - this.canvasHeight + this.padding);
        var nextY = this.scrollY + this._scrollSpeed;
        // this.scrollY = Math.clamp(nextY, 0, maxScrollableY);
        this.scrollY = (!this._limitScroll) ? nextY : Math.clamp(nextY, 0, maxScrollableY);
        // this.scrollY = this.scrollY + velocity;
      });
    }
  },

  /**
   * 改行付きで文を追加
   * @instance
   * @memberof phina.ui.ScrollableLabelArea
   *
   * @param {string} line
   * @return {this} - return instance
   */
  addLine: function(line) {
    this.text += "\n"+line;
    return this;
  },

  _accessor: {

    /**
     * エリアのheightを返します。
     *
     * @instance
     * @memberof phina.ui.ScrollableLabelArea
     * @member {number}
     * @readonly
     */
    textAreaHeight: {
      get: function() {
        // var fakeTextHeight = this.canvas.context.measureText('M').width;
        // return this.getLines().length * fakeTextHeight;
        return this.getLines().length * this.lineSize; // 注:lineSizeは行の高さだが大雑把
      }
    },

    /**
     * canvas本体のheightを返します。
     *
     * @instance
     * @memberof phina.ui.ScrollableLabelArea
     * @member {number}
     * @readonly
     */
    canvasHeight: {
      get: function () { return this.calcCanvasHeight(); },
    },
  },

});