
import TWEEN from '@tweenjs/tween.js';


interface AirWindow  extends Window {
  air: any;
  curCtx: any;
  curImages: any;
}
declare let window: AirWindow;

class Sprite {
  x = 0;
  y = 0;
  color = '';
  radius = 0;
  alive = false;
  margin = 0;
  angle = 0;
  ctx;

  constructor(ctx = null) {
    if (!ctx) {
      this.ctx = window.curCtx;
    } else {
      this.ctx = ctx;
    }
  }
  update($event) {
    this.draw();
  }
  draw() {

  }

}




export class MySprite extends Sprite {

  _width = 0;
  _height = 0;
  _anchorX = 0;
  _anchorY = 0;
  _offX = 0;
  _offY = 0;
  scaleX = 1;
  scaleY = 1;
  _alpha = 1;
  rotation = 0;
  visible = true;
  skewX = 0;
  skewY = 0;

  _shadowFlag = false;
  _shadowColor;
  _shadowOffsetX = 0;
  _shadowOffsetY = 0;
  _shadowBlur = 5;

  _radius = 0;

  children = [this];

  childDepandVisible = true;
  childDepandAlpha = false;

  img;
  _z = 0;

  _showRect;


  _bitmapFlag = false;
  _offCanvas;
  _offCtx;
  isCircleStyle = false; // 切成圆形
  parent;

  _maskSprArr = [];
  _maskType = "destination-in" // destination-out


  init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {

    if (imgObj) {

      this.img = imgObj;

      this.width = this.img.width;
      this.height = this.img.height;

      // this.img.setAttribute("crossOrigin",'Anonymous')

    }

    this.anchorX = anchorX;
    this.anchorY = anchorY;
  }


  setShowRect(rect) {
    this._showRect = rect;
  }

  setShadow(offX, offY, blur, color = 'rgba(0, 0, 0, 0.3)') {

    this._shadowFlag = true;
    this._shadowColor = color;
    this._shadowOffsetX = offX;
    this._shadowOffsetY = offY;
    this._shadowBlur = blur;

  }

  setRadius(r) {
    this._radius = r;
  }

  addMaskSpr(spr) {
    this._maskSprArr.push(spr);
    this._createOffCtx();
  }

  setMaskType(t) {
    this._maskType = t;
  }

  _createOffCtx() {
    if (!this._offCtx) {
      this._offCanvas = document.createElement('canvas'); // 创建一个新的canvas
      this._offCanvas.width = this.width; // 创建一个正好包裹住一个粒子canvas
      this._offCanvas.height = this.height;
      this._offCtx = this._offCanvas.getContext('2d');
    }
  }

  update($event = null) {
    if (!this.visible && this.childDepandVisible) {
      return;
    }

    this.draw();

  }
  draw() {

    this.ctx.save();

    this.drawInit();

    this.updateChildren();

    this.ctx.restore();


  }

  drawInit() {

    this.ctx.translate(this.x, this.y);

    this.ctx.rotate(this.rotation * Math.PI / 180);

    this.ctx.scale(this.scaleX, this.scaleY);

    this.ctx.globalAlpha = this.alpha;

    this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0);


  }

  drawSelf() {

    if (this._shadowFlag) {

      this.ctx.shadowOffsetX = this._shadowOffsetX;
      this.ctx.shadowOffsetY = this._shadowOffsetY;
      this.ctx.shadowBlur = this._shadowBlur;
      this.ctx.shadowColor = this._shadowColor;
    } else {
      this.ctx.shadowOffsetX = 0;
      this.ctx.shadowOffsetY = 0;
      this.ctx.shadowBlur = null;
      this.ctx.shadowColor = null;
    }


    if (this._radius) {

      const r = this._radius;
      const w = this.width;
      const h = this.height;

      this.ctx.beginPath()
      this._roundRect(-w / 2, - h / 2, w, h, r * 1 || 0);
      this.ctx.clip();
    }

    if (this.isCircleStyle) {
      this.ctx.beginPath()
      this.ctx.arc(0, 0, Math.max(this.width, this.height) / 2, 0, Math.PI * 2, false); // 圆形
      this.ctx.clip();
    }



    if (this.img) {

      if (this._showRect) {
        const rect = this._showRect;
        this.ctx.drawImage(this.img, rect.x, rect.y, rect.width, rect.height, this._offX + rect.x, this._offY + rect.y, rect.width, rect.height);
      } else {
        if (this._offCtx) {
          this._offScreenRender();
        } else {
          this.ctx.drawImage(this.img, this._offX, this._offY);
        }
      }
    }
  }

  _offScreenRender() {

    this._offCtx.save();

    this._offCtx.clearRect(0, 0, this.width, this.height);



    this._offCtx.drawImage(this.img, 0, 0);
    this._offCtx.globalCompositeOperation = this._maskType;

    if (this._maskSprArr && this._maskSprArr.length > 0) {
      
      for (let i=0; i<this._maskSprArr.length; i++) {
        this._maskSprArr[i].ctx = this._offCtx;
        this._maskSprArr[i].update();
      }
    
    }



    this.ctx.drawImage(this._offCanvas, this._offX, this._offY);

    this._offCtx.restore();

  }

  _roundRect(x, y, w, h, r) {
    const min_size = Math.min(w, h);
    if (r > min_size / 2) r = min_size / 2;
    // 开始绘制
    const ctx = this.ctx;
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.arcTo(x + w, y, x + w, y + h, r);
    ctx.arcTo(x + w, y + h, x, y + h, r);
    ctx.arcTo(x, y + h, x, y, r);
    ctx.arcTo(x, y, x + w, y, r);
    ctx.closePath();
  }


    updateChildren() {

    if (this.children.length <= 0) { return; }

    for (const child of  this.children) {
      if (child === this) {
        if (this.visible) {
          this.drawSelf();
        }
      } else {
        child.update();
      }
    }
  }


  load(url, anchorX = 0.5, anchorY = 0.5) {

    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.onerror = reject;
      img.src = url;
    }).then(img => {

      this.init(img, anchorX, anchorY);
      return img;
    });
  }

  addChild(child, z = 1) {
    if (this.children.indexOf(child) === -1) {
      this.children.push(child);
      child._z = z;
      child.parent = this;
    }

    this.children.sort((a, b) => {
      return a._z - b._z;
    });


    if (this.childDepandAlpha) {
      child.alpha = this.alpha;
    }

    child.ctx = this.ctx;

  }
  removeChild(child) {
    const index = this.children.indexOf(child);
    if (index !== -1) {
      this.children.splice(index, 1);
    }
  }

  removeChildren() {
    for (let i = 0; i < this.children.length; i++) {
      if (this.children[i]) {
        if (this.children[i] !== this) {
          this.children.splice(i, 1);
          i --;
        }
      }
    }
  }

  _changeChildAlpha(alpha) {
    for (const child of  this.children) {
      if (child !== this) {
        child.alpha = alpha;
      }
    }
  }


  set bitmapFlag(v) {
    this._bitmapFlag = v;
  }
  get bitmapFlag() {
    return this._bitmapFlag;
  }
  set alpha(v) {
    this._alpha = v;
    if (this.childDepandAlpha) {
      this._changeChildAlpha(v);
    }
  }
  get alpha() {
    return this._alpha;
  }
  set width(v) {
    this._width = v;
    this.refreshAnchorOff();
  }
  get width() {
    return this._width;
  }
  set height(v) {
    this._height = v;
    this.refreshAnchorOff();
  }
  get height() {
    return this._height;
  }
  set anchorX(value) {
    this._anchorX = value;
    this.refreshAnchorOff();
  }
  get anchorX() {
    return this._anchorX;
  }
  set anchorY(value) {
    this._anchorY = value;
    this.refreshAnchorOff();
  }
  get anchorY() {
    return this._anchorY;
  }
  refreshAnchorOff() {
    this._offX = -this._width * this.anchorX;
    this._offY = -this._height * this.anchorY;
  }

  setScaleXY(value) {
    this.scaleX = this.scaleY = value;
  }

  getBoundingBox() {

    const getParentData = (item) => {

      let px = item.x;
      let py = item.y;

      let sx = item.scaleX;
      let sy = item.scaleY;
      const parent = item.parent;
      if (parent) {

        const obj = getParentData(parent);

        const _x = obj.px;
        const _y = obj.py;
        const _sx = obj.sx;
        const _sy = obj.sy;

        px = _x + item.x * _sx;
        py = _y + item.y * _sy;

        sx *= _sx;
        sy *= _sy;

      }

      return {px, py, sx, sy};
    };


    const data = getParentData(this);


    const x = data.px + this._offX * Math.abs(data.sx);
    const y = data.py + this._offY * Math.abs(data.sy);
    const width = this.width * Math.abs(data.sx);
    const height = this.height * Math.abs(data.sy);

    return {x, y, width, height};
  }

}





export class ColorSpr extends MySprite {

  r = 0;
  g = 0;
  b = 0;

  createGSCanvas() {

    if (!this.img) {
      return;
    }



    const rect = this.getBoundingBox();
    if (rect.width <= 1 || rect.height <= 1) {
      return;
    }


    const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
    for ( let i = 0; i < c.height; i++) {
      for ( let j = 0; j < c.width; j++) {

        const x = (i * 4) * c.width + ( j * 4 );
        const r = c.data[x];
        const g = c.data[x + 1];
        const b = c.data[x + 2];

        c.data[x] = this.r;
        c.data[x + 1] = this.g;
        c.data[x + 2] = this.b;



        // c.data[x] = c.data[x + 1] = c.data[x + 2] = (r + g + b) / 3 ;
        // // c.data[x + 3] = 255;
      }
    }

    this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
  }


  drawSelf() {
    super.drawSelf();
    this.createGSCanvas();
  }

}


export class GrayscaleSpr extends MySprite {

  grayScale = 120;

  createGSCanvas() {

    if (!this.img) {
      return;
    }



    const rect = this.getBoundingBox();

    const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
    for ( let i = 0; i < c.height; i++) {
      for ( let j = 0; j < c.width; j++) {

        const x = (i * 4) * c.width + ( j * 4 );
        const r = c.data[x];
        const g = c.data[x + 1];
        const b = c.data[x + 2];
        // const a = c.data[x + 3];

        c.data[x] = c.data[x + 1] = c.data[x + 2] = this.grayScale; // (r + g + b) / 3;
        // c.data[x + 3] = 255;
      }
    }

    this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
  }


  drawSelf() {
    super.drawSelf();
    this.createGSCanvas();
  }

}






export class BitMapLabel extends MySprite {


  labelArr;
  baseUrl;

  setText(data, text) {

    this.labelArr = [];

    const labelArr = [];
    const tmpArr = text.split('');
    let totalW = 0;
    let h = 0;
    for (const tmp of tmpArr) {

      const label = new MySprite(this.ctx);
      label.init(data[tmp], 0);
      this.addChild(label);
      labelArr.push(label);

      totalW += label.width;
      h = label.height;
    }


    this.width = totalW;
    this.height = h;

    let offX = -totalW / 2;
    for (const label of  labelArr) {
      label.x = offX;
      offX += label.width;
    }

    this.labelArr = labelArr;
  }

}




export class Label extends MySprite {

  text: string;
  // fontSize:String = '40px';
  fontName = 'Verdana';
  textAlign = 'left';
  textBaseline = 'middle';
  fontSize = 40;
  fontColor = '#000000';
  fontWeight = 900;
  _maxWidth;
  outline = 0;
  outlineColor = '#ffffff';

  // _shadowFlag = false;
  // _shadowColor;
  // _shadowOffsetX;
  // _shadowOffsetY;
  // _shadowBlur;

  _outlineFlag = false;
  _outLineWidth;
  _outLineColor;


  constructor(ctx = null) {
    super(ctx);
    this.init();
  }

  refreshSize() {

    this.ctx.save();

    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
    this.ctx.textAlign = this.textAlign;
    this.ctx.textBaseline = this.textBaseline;
    this.ctx.fontWeight = this.fontWeight;

    this._width = this.ctx.measureText(this.text).width;
    this._height = this.fontSize;
    this.refreshAnchorOff();

    this.ctx.restore();

  }

  setMaxSize(w) {

    this._maxWidth = w;
    this.refreshSize();
    if (this.width >= w) {
      this.scaleX *= w / this.width;
      this.scaleY *= w / this.width;
    }
  }

  show(callBack = null) {

    this.visible = true;

    if (this.alpha >= 1) {
      return;
    }

    const tween = new TWEEN.Tween(this)
      .to({ alpha: 1 }, 800)
      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
      .onComplete(() => {
        if (callBack) {
          callBack();
        }
      })
      .start(); // Start the tween immediately.
  }

  // setShadow(offX = 0, offY = 2, blur = 2, color = 'rgba(0, 0, 0, 0.2)') {
  //
  //   this._shadowFlag = true;
  //   this._shadowColor = color;
  //   // 将阴影向右移动15px，向上移动10px
  //   this._shadowOffsetX = 5;
  //   this._shadowOffsetY = 5;
  //   // 轻微模糊阴影
  //   this._shadowBlur = 5;
  // }

  setOutline(width = 5, color = '#ffffff') {

    this._outlineFlag = true;
    this._outLineWidth = width;
    this._outLineColor = color;

  }

  drawText() {

    // console.log('in drawText', this.text);

    if (!this.text) { return; }


    // if (this._shadowFlag) {
    //
    //   this.ctx.shadowColor = this._shadowColor;
    //   // 将阴影向右移动15px，向上移动10px
    //   this.ctx.shadowOffsetX = this._shadowOffsetX;
    //   this.ctx.shadowOffsetY = this._shadowOffsetY;
    //   // 轻微模糊阴影
    //   this.ctx.shadowBlur = this._shadowBlur;
    // }



    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
    this.ctx.textAlign = this.textAlign;
    this.ctx.textBaseline = 'middle';
    this.ctx.fontWeight = this.fontWeight;

    if (this._outlineFlag) {
      this.ctx.lineWidth = this._outLineWidth;
      this.ctx.strokeStyle = this._outLineColor;
      this.ctx.strokeText(this.text, 0, 0);
    }

    this.ctx.fillStyle = this.fontColor;



    if (this.outline > 0) {
      this.ctx.lineWidth = this.outline;
      this.ctx.strokeStyle = this.outlineColor;
      this.ctx.strokeText(this.text, 0, 0);

    }

    this.ctx.fillText(this.text, 0, 0);


  }


  drawSelf() {
    super.drawSelf();
    this.drawText();
  }

}

export class RichTextOld extends Label {

  textArr = [];
  fontSize = 40;

  setText(text: string, words) {

    let newText = text;
    for (const word of words) {

      const re = new RegExp(word, 'g');
      newText = newText.replace( re, `#${word}#`);
      // newText = newText.replace(word, `#${word}#`);
    }

    this.textArr = newText.split('#');
    this.text = newText;

    // this.setSize();
  }

  refreshSize() {

    this.ctx.save();

    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
    this.ctx.textAlign = this.textAlign;
    this.ctx.textBaseline = 'middle';
    this.ctx.fontWeight = this.fontWeight;

    let curX = 0;
    for (const text of this.textArr) {
      const w = this.ctx.measureText(text).width;
      curX += w;
    }

    this.width = curX;
    this.height = this.fontSize;
    this.refreshAnchorOff();

    this.ctx.restore();

  }


  show(callBack = null) {

    // console.log(' in show ');
    this.visible = true;
    // this.alpha = 0;

    const tween = new TWEEN.Tween(this)
      .to({ alpha: 1 }, 800)
      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
      .onComplete(() => {
        if (callBack) {
          callBack();
        }
      })
      .start(); // Start the tween immediately.

  }



  drawText() {

    // console.log('in drawText', this.text);

    if (!this.text) { return; }


    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
    this.ctx.textAlign = this.textAlign;
    this.ctx.textBaseline = 'middle';
    this.ctx.fontWeight = 900;

    this.ctx.lineWidth = 5;
    this.ctx.strokeStyle = '#ffffff';
    // this.ctx.strokeText(this.text, 0, 0);

    this.ctx.fillStyle = '#000000';
    // this.ctx.fillText(this.text, 0, 0);


    let curX = 0;
    for (let i = 0; i < this.textArr.length; i++) {

      const w = this.ctx.measureText(this.textArr[i]).width;

      if ((i + 1) % 2 === 0) {
        this.ctx.fillStyle = '#c8171e';
      } else {
        this.ctx.fillStyle = '#000000';
      }

      this.ctx.fillText(this.textArr[i], curX, 0);
      curX += w;

    }


  }

}






export class RichText extends Label {


  topH = 0;
  disH = 10;
  offW = 10;
  row = 1;
  textBaseline = "middle";

  isShowWordBg = false;
  wordBgData;

  constructor(ctx?: any) {
    super(ctx);

    // this.dataArr = dataArr;
  }

  getLineNum() {
    this.drawSelf();
    return this.row;
  }

  getAreaHeight() {
    this.drawSelf();
    const tmpTextH = this.row * this.fontSize;
    const tmpDisH = (this.row - 1) * this.disH
    return tmpTextH + tmpDisH;
  }

  getSubTextRectGroup(text, targetIndex = 0) {

    console.log('!!!wordBgData: ', this.wordBgData);

    const rectGroup = [];
    const subTextArr = text.split(' ');
    let baseIndex = targetIndex;
    console.log('subTextArr: ', subTextArr);
    for (let i=0; i<subTextArr.length; i++) {
      const subText = subTextArr[i];
      if (!subText) {
        continue;
      }
      const subData = this.getSubTextRect(subText, baseIndex)
      if (subData) {
        console.log('baseIndex1 : ', baseIndex);
        rectGroup.push(subData);
        baseIndex = Number( subData.index ) + subData.text.length;
        console.log('baseIndex2 : ', baseIndex);
      }
    }

    return rectGroup;
  }

  getSubTextRect(subText, targetIndex=0) {

 

    subText = subText.trim();
    if (!subText) {
      return;
    }
    

    this.isShowWordBg = true;
    this.update();


    const tmpLabel = new RichText();
    tmpLabel.fontSize = this.fontSize;
    tmpLabel.fontName = this.fontName;
    tmpLabel.textAlign = this.textAlign;
    tmpLabel.textBaseline = this.textBaseline;
    tmpLabel.fontWeight = this.fontWeight;
    tmpLabel.width = this.width;
    tmpLabel.height = this.height;
    // console.log('subText: ', subText);
    // console.log('this.text: ', this.text);
    // console.log('targetIndex: ', targetIndex);
    // const indexArr = searchSubStr(this.text, subText);
    // console.log('indexArr: ', indexArr);
    // const index = indexArr[targetIndex];

    const index = this.text.indexOf(subText, targetIndex);

    // console.log('index: ', index);
    if (index == -1) {
      return;
    }
    
    // console.log('this.wordBgData: ', this.wordBgData);
    // const index = this.text.indexOf(subText);

    // console.log('!!!index: ', index);

    const data = this.wordBgData[index.toString()];
    // console.log('!!!wordBgData: ', this.wordBgData);
    console.log('!!!data: ', data);

    return data;



  }

  drawText() {

    if (!this.text) {
      return;
    }

    let curCtx = this.ctx;

    if (this._offCtx) {



      this._offCtx.save();
      this._offCtx.clearRect(0, 0, this.width, this.height);


      curCtx = this._offCtx;
    }

    curCtx.font = `${this.fontSize}px ${this.fontName}`;
    curCtx.textAlign = this.textAlign;
    curCtx.textBaseline = this.textBaseline;
    curCtx.fontWeight = this.fontWeight;
    curCtx.fillStyle = this.fontColor;


    const selfW = this.width * this.scaleX;


    const chr = this.text.split(' ');
    let temp = '';
    const row = [];
    const w = selfW - this.offW * 2;
    const disH = (this.fontSize + this.disH) * this.scaleY;

    let isPushWordData = false;
    if (this.isShowWordBg && !this.wordBgData) {
      this.wordBgData = {};
      isPushWordData = true;
    }

    let wordIndex = -1;
    for (const c of  chr) {

      if (isPushWordData) {
      }


      if (c == '\n') {
        row.push(temp);
        temp = '';
      } else if (curCtx.measureText(temp).width < w && curCtx.measureText(temp + (c)).width <= w) {
        temp += ' ' + c;
      } else {
        row.push(temp);
        temp = ' ' + c;
      }

      if (isPushWordData) {
        wordIndex = this.text.indexOf(c, wordIndex+1);
        const key = wordIndex.toString();
        const rectX = curCtx.measureText(temp).width
        const rectY = ( row.length ) * disH / this.scaleY
        const rectW = curCtx.measureText(c).width;
        const rectH = this.fontSize;
        this.wordBgData[key] = {rect: {x: rectX, y: rectY, width: rectW, height: rectH}, text: c, index: wordIndex}
      }

    }
    row.push(temp);
    
    this.row = row.length;

    const x = 0;
    const y = this.topH//-row.length * disH / 2;
    // for (let b = 0 ; b < row.length; b++) {
    //   curCtx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
    // }


    if (this._outlineFlag) {
      curCtx.lineWidth = this._outLineWidth;
      curCtx.strokeStyle = this._outLineColor;
      for (let b = 0 ; b < row.length; b++) {
        curCtx.strokeText(row[b], x, y + ( b + 0 ) * disH / this.scaleY ); // 每行字体y坐标间隔20
      }
      // curCtx.strokeText(this.text, 0, 0);
    }


    // curCtx.fillStyle = '#ff7600';
    for (let b = 0 ; b < row.length; b++) {
      curCtx.fillText(row[b], x, y + ( b + 0 ) * disH / this.scaleY ); // 每行字体y坐标间隔20
    }


    if (this._offCtx) { 


      this._offCtx.globalCompositeOperation = this._maskType;

      if (this._maskSprArr && this._maskSprArr.length > 0) {
        
        for (let i=0; i<this._maskSprArr.length; i++) {
          this._maskSprArr[i].ctx = this._offCtx;
          this._maskSprArr[i].update();
        }
        // console.log('aaaaa1');
      }

      this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
      this._offCtx.restore();
    }

  }


  drawSelf() {
    super.drawSelf();
    this.drawText();
  }

}




export class LineRect extends MySprite {

  lineColor = '#ffffff';
  lineWidth = 10;

  setSize(w, h) {
    this.width = w;
    this.height = h;
  }

  drawLine() {

    this.ctx.beginPath();
    this.ctx.moveTo(this._offX, this._offY);
    this.ctx.lineTo(this._offX + this.width, this._offY);
    this.ctx.lineTo(this._offX + this.width, this._offY + this.height);
    this.ctx.lineTo(this._offX, this._offY + this.height);
    this.ctx.closePath();

    this.ctx.lineWidth = this.lineWidth;
    // this.ctx.fillStyle = "rgb(2,33,42)";  //指定填充颜色
    // this.ctx.fill();   //对多边形进行填充

    this.ctx.strokeStyle = this.lineColor; // "#ffffff";
    this.ctx.stroke();

  }


  drawSelf() {
    super.drawSelf();
    this.drawLine();
  }
}


export class ShapeRect extends MySprite {

  fillColor = '#FF0000';

  setSize(w, h) {
    this.width = w;
    this.height = h;

    // console.log('w:', w);
    // console.log('h:', h);
  }

  drawShape() {

    if (this._offCtx) {
      this._offCtx.save();
      this._offCtx.clearRect(0, 0, this.width, this.height);
  
      this._offCtx.fillStyle = this.fillColor;
      this._offCtx.fillRect(this._offX, this._offY, this.width, this.height);
      this._offCtx.globalCompositeOperation = this._maskType;

      if (this._maskSprArr && this._maskSprArr.length > 0) {
        for (let i=0; i<this._maskSprArr.length; i++) {
          this._maskSprArr[i].ctx = this._offCtx;
          this._maskSprArr[i].update();
        }
      }
  
      this.ctx.drawImage(this._offCanvas, this._offX, this._offY);

      this._offCtx.restore();

    } else {
      this.ctx.fillStyle = this.fillColor;
      this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
  
    }
  
  }


  drawSelf() {
    super.drawSelf();
    this.drawShape();
  }
}



export class ShapeCircle extends MySprite {

  fillColor = '#FF0000';
  radius = 0;


  setRadius(r) {
    this.anchorX = this.anchorY = 0.5;

    this.radius = r;
    this.width = r * 2;
    this.height = r * 2;
  }

  drawShape() {

    this.ctx.beginPath();
    this.ctx.fillStyle = this.fillColor;
    this.ctx.arc(0, 0, this.radius, 0, angleToRadian(360));
    this.ctx.fill();
  }

  drawSelf() {
    super.drawSelf();
    this.drawShape();
  }
}

export class ShapeRectNew extends MySprite {


  radius = 0;
  fillColor = '#ffffff';
  strokeColor = '#000000';
  fill = true;
  stroke = false;
  lineWidth = 1;

  setSize(w, h, r) {
    this.width = w;
    this.height = h;
    this.radius = r;
  }

  setOutLine(color, lineWidth) {
    this.stroke = true;
    this.strokeColor = color;
    this.lineWidth = lineWidth;
  }


  drawShape() {

    const ctx = this.ctx;
    const width = this.width;
    const height = this.height;
    const radius = this.radius;

    ctx.save();
    ctx.beginPath(0);
    // 从右下角顺时针绘制，弧度从0到1/2PI
    ctx.arc(width - radius + this._offX, height - radius + this._offY, radius, 0, Math.PI / 2);

    // 矩形下边线
    ctx.lineTo(radius + this._offX, height + this._offY);

    // 左下角圆弧，弧度从1/2PI到PI
    ctx.arc(radius + this._offX, height - radius + this._offY, radius, Math.PI / 2, Math.PI);

    // 矩形左边线
    ctx.lineTo(0 + this._offX, radius + this._offY);

    // 左上角圆弧，弧度从PI到3/2PI
    ctx.arc(radius + this._offX, radius + this._offY, radius, Math.PI, Math.PI * 3 / 2);

    // 上边线
    ctx.lineTo(width - radius + this._offX, 0 + this._offY);

    // 右上角圆弧
    ctx.arc(width - radius + this._offX, radius + this._offY, radius, Math.PI * 3 / 2, Math.PI * 2);

    // 右边线
    ctx.lineTo(width + this._offX, height - radius + this._offY);
    ctx.closePath();

    if (this.fill) {
      ctx.fillStyle = this.fillColor;
      ctx.fill();
    }
    if (this.stroke) {
      ctx.lineWidth = this.lineWidth;
      ctx.strokeStyle = this.strokeColor;
      ctx.stroke();
    }
    ctx.restore();

  }


  drawSelf() {
    super.drawSelf();
    this.drawShape();
  }

}
export class MyAnimation extends MySprite {


  frameArr = [];
  frameIndex = 0;
  playFlag = false;
  lastDateTime;
  curDelay = 0;


  loop = false;
  playEndFunc;
  delayPerUnit = 0.07;

  restartFlag = false;
  reverseFlag = false;

  addFrameByImg(img) {

    const spr = new MySprite(this.ctx);
    spr.init(img);
    this._refreshSize(img);

    spr.visible = false;

    this.addChild(spr);
    this.frameArr.push(spr);

    this.frameArr[this.frameIndex].visible = true;
  }

  addFrameByUrl(url) {

    const spr = new MySprite(this.ctx);
    spr.load(url).then(img => {

      this._refreshSize(img);
    });
    spr.visible = false;

    this.addChild(spr);
    this.frameArr.push(spr);

    this.frameArr[this.frameIndex].visible = true;
  }

  _refreshSize(img: any) {

    if (this.width < img.width) {
      this.width = img.width;
    }
    if (this.height < img.height) {
      this.height = img.height;
    }
  }



  play() {
    this.playFlag = true;
    this.lastDateTime = new Date().getTime();
  }

  stop() {
    this.playFlag = false;
  }


  replay() {
    this.restartFlag = true;
    this.play();
  }


  reverse() {
    this.reverseFlag = !this.reverseFlag;
    this.frameArr.reverse();
    this.frameIndex = 0;
  }

  showAllFrame() {
    for (const frame of this.frameArr ) {
      frame.alpha = 1;
    }
  }

  hideAllFrame() {
    for (const frame of this.frameArr) {
      frame.alpha = 0;
    }
  }


  playEnd() {

    this.playFlag = false;
    this.curDelay = 0;

    this.frameArr[this.frameIndex].visible = true;

    if (this.playEndFunc) {
      const func = this.playEndFunc;
      this.playEndFunc = null;
      func();
    }
  }

  updateFrame() {
    if (this.frameArr[this.frameIndex]) {
      this.frameArr[this.frameIndex].visible = false;
    }

    this.frameIndex ++;
    if (this.frameIndex >= this.frameArr.length) {
      if (this.loop) {
        this.frameIndex = 0;
      } else if (this.restartFlag) {
        this.restartFlag = false;
        this.frameIndex = 0;
      } else {
        this.frameIndex -- ;
        this.playEnd();
        return;
      }
    }

    this.frameArr[this.frameIndex].visible = true;

  }




  _updateDelay(delay) {

    this.curDelay += delay;
    if (this.curDelay < this.delayPerUnit) {
      return;
    }
    this.curDelay -= this.delayPerUnit;
    this.updateFrame();
  }

  _updateLastDate() {
    if (!this.playFlag) { return; }

    let delay = 0;
    if (this.lastDateTime) {
      delay = (new Date().getTime() - this.lastDateTime) / 1000;
    }
    this.lastDateTime = new Date().getTime();
    this._updateDelay(delay);
  }

  update($event: any = null) {
    super.update($event);
    this._updateLastDate();
  }


}




// --------===========  util func  =============-------------


export function tweenChange(item, obj, time = 0.8, callBack = null, easing = null, update = null) {

  const tween = createTween(item, obj, time, callBack, easing, update)
  tween.start();

  return tween;
}

export function createTween(item, obj, time = 0.8, callBack = null, easing = null, update = null) {

  const tween = new TWEEN.Tween(item).to(obj, time * 1000);

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }
  if (update) {
    tween.onUpdate( (a, b) => {
      update(a, b);
    });
  }
  return tween;
}

export function tweenQueue(arr) {

  const showOneTween = () => {
    const tween = arr.shift();
    if (arr.length > 0) {
      tween.onComplete( () => {
        showOneTween();
      });
    }
    tween.start();
  };

  showOneTween();
}




export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = null, loop = false) {

  const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000);

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  } else if (loop) {
    const speed = (rotation - item.rotation) / time;
    tween.onComplete(() => {
      item.rotation %= 360;
      rotateItem(item, item.rotation + speed * time, time, null, easing, true);
    });
  }

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}



export function scaleItem(item, scale, time = 0.8, callBack = null, easing = null) {

  const tween = new TWEEN.Tween(item).to({ scaleX: scale, scaleY: scale}, time * 1000);

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }

  tween.start();
  return tween;
}


export function moveItem(item, x, y, time = 0.8, callBack = null, easing = null) {

  const tween = new TWEEN.Tween(item).to({ x, y}, time * 1000);

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }

  tween.start();

  return tween;
}







export function endShow(item, s = 1) {

  item.scaleX = item.scaleY = 0;
  item.alpha = 0;

  const tween = new TWEEN.Tween(item)
    .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
    .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
    .onComplete(() => {

    })
    .start();
}

export function hideItem(item, time = 0.8, callBack = null, easing = null) {

  if (item.alpha === 0) {
    return;
  }

  const tween = new TWEEN.Tween(item)
    .to({alpha: 0}, time * 1000)
    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
    .onComplete(() => {
      if (callBack) {
        callBack();
      }
    });

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}


export function showItem(item, time = 0.8, callBack = null, easing = null) {

  if (item.alpha === 1) {
    if (callBack) {
      callBack();
    }
    return;
  }

  item.visible = true;
  const tween = new TWEEN.Tween(item)
    .to({alpha: 1}, time * 1000)
    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
    .onComplete(() => {
      if (callBack) {
        callBack();
      }
    });

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}


export function alphaItem(item, alpha, time = 0.8, callBack = null, easing = null) {


  const tween = new TWEEN.Tween(item)
    .to({alpha}, time * 1000)
    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
    .onComplete(() => {
      if (callBack) {
        callBack();
      }
    });

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}


export function showStar(item, time = 0.8, callBack = null, easing = null) {


  const tween = new TWEEN.Tween(item)
    .to({alpha: 1, scale: 1}, time * 1000)
    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
    .onComplete(() => {
      if (callBack) {
        callBack();
      }
    });

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}


export function randomSortByArr(arr) {
  const newArr = [];
  const tmpArr = arr.concat();
  while (tmpArr.length > 0) {
    const randomIndex = Math.floor( tmpArr.length * Math.random() );
    newArr.push(tmpArr[randomIndex]);
    tmpArr.splice(randomIndex, 1);
  }
  return newArr;
}


export function radianToAngle(radian) {
  return radian * 180 / Math.PI;
  // 角度 = 弧度 * 180 / Math.PI;
}

export function angleToRadian(angle) {
  return angle * Math.PI / 180;
  // 弧度= 角度 * Math.PI / 180;
}

export function getPosByAngle(angle, len) {

  const radian = angle * Math.PI / 180;
  const x = Math.sin(radian) * len;
  const y = Math.cos(radian) * len;

  return {x, y};

}

export function getAngleByPos(px, py, mx, my) {

  const x = Math.abs(px - mx);
  const y = Math.abs(py - my);

  const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
  const cos = y / z;
  const radina = Math.acos(cos); // 用反三角函数求弧度
  let angle = Math.floor(180 / (Math.PI / radina) * 100) / 100; // 将弧度转换成角度

  if (mx > px && my > py) {// 鼠标在第四象限
    angle = 180 - angle;
  }
  if (mx === px && my > py) {// 鼠标在y轴负方向上
    angle = 180;
  }
  if (mx > px && my === py) {// 鼠标在x轴正方向上
    angle = 90;
  }
  if (mx < px && my > py) {// 鼠标在第三象限
    angle = 180 + angle;
  }
  if (mx < px && my === py) {// 鼠标在x轴负方向
    angle = 270;
  }
  if (mx < px && my < py) {// 鼠标在第二象限
    angle = 360 - angle;
  }

  // console.log('angle: ', angle);
  return angle;

}


export function removeItemFromArr(arr, item) {
  if (!arr || !item) {
    return;
  }
  const index = arr.indexOf(item);
  if (index !== -1) {
    arr.splice(index, 1);
  }
}






export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer = 1, callBack = null, easing = null) {

  const r = getPosDistance(item.x, item.y, x0, y0);
  let a = getAngleByPos(item.x, item.y, x0, y0);
  a += 90;
  const obj = {r, a};

  item._circleAngle = a;
  const targetA = a + addR;

  const tween = new TWEEN.Tween(item).to({_circleAngle: targetA}, time * 1000);

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }

  tween.onUpdate( (item, progress) => {

    // console.log(item._circleAngle);
    const r = obj.r;
    const a = item._circleAngle;

    const x = x0 + r * xPer * Math.cos(a * Math.PI / 180);
    const y = y0 + r * yPer * Math.sin(a * Math.PI / 180);

    item.x = x;
    item.y = y;

    // obj.a ++;
  });

  tween.start();
}


export function getPosDistance(sx, sy, ex, ey) {

  const _x = ex - sx;
  const _y = ey - sy;
  const len = Math.sqrt( Math.pow(_x, 2) + Math.pow(_y, 2) );
  return len;
}

export function delayCall(second, callback) {
  const tween = new TWEEN.Tween(this)
    .delay(second * 1000)
    .onComplete(() => {
      if (callback) {
        callback();
      }
    })
    .start();
}


export function formatTime(fmt, date) {

  // "yyyy-MM-dd HH:mm:ss";

  const o = {
    'M+': date.getMonth() + 1, // 月份
    'd+': date.getDate(), // 日
    'h+': date.getHours(), // 小时
    'm+': date.getMinutes(), // 分
    's+': date.getSeconds(), // 秒
    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds() // 毫秒
  };
  if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); }
  for (const k in o) {
    if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1)
      ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
    }
  }
  return fmt;
}

export function getMinScale(item, maxW, maxH = null) {
  if (!maxH) {
    maxH = maxW;
  }

  const sx = maxW / item.width;
  const sy = maxH / item.height;
  const minS = Math.min(sx, sy);

  return minS;
}



export function jelly(item, time = 0.7) {


  if (item.jellyTween) {
    TWEEN.remove(item.jellyTween);
  }

  const t = time / 9;
  const baseSX = item.scaleX;
  const baseSY = item.scaleY;
  let index = 0;

  const run = () => {
    if (index >= arr.length) {
      item.jellyTween = null;
      return;
    }
    const data = arr[index];
    const t = tweenChange(item, {scaleX: data[0], scaleY: data[1]}, data[2], () => {
      index ++;
      run();
    }, TWEEN.Easing.Sinusoidal.InOut);
    item.jellyTween = t;
  };

  const arr = [
    [baseSX * 1.1, baseSY * 0.9, t],
    [baseSX * 0.98, baseSY * 1.02, t * 2],
    [baseSX * 1.02, baseSY * 0.98, t * 2],
    [baseSX * 0.99, baseSY * 1.01, t * 2],
    [baseSX * 1.0, baseSY * 1.0, t * 2],
  ];


  run();
}

export function jellyPop(item, time) {

  if (item.jellyTween) {
    TWEEN.remove(item.jellyTween);
  }

  const t = time / 6;
  const baseSX = item.scaleX;
  const baseSY = item.scaleY;
  let index = 0;

  const run = () => {
    if (index >= arr.length) {
      item.jellyTween = null;
      return;
    }
    const data = arr[index];
    const t = tweenChange(item, {scaleX: data[0], scaleY: data[1]}, data[2], () => {
      index ++;
      run();
    }, TWEEN.Easing.Sinusoidal.InOut);
    item.jellyTween = t;
  };


  const arr = [
    [baseSX * 1.3, baseSY * 1.3, t],
    [baseSX * 0.85, baseSY * 0.85, t * 1],
    [baseSX * 1.1, baseSY * 1.1, t * 1],
    [baseSX * 0.95, baseSY * 0.95, t * 1],
    [baseSX * 1.02, baseSY * 1.02, t * 1],
    [baseSX * 1, baseSY * 1, t * 1],
  ];


  item.setScaleXY(0);
  run();

}



export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) {


  for (let i = 0; i < num; i ++) {

    const particle = new MySprite();
    particle.init(img);
    particle.x = pos.x;
    particle.y = pos.y;
    parent.addChild(particle);

    const randomR = 360 * Math.random();
    particle.rotation = randomR;

    const randomS = 0.3 + Math.random() * 0.7;
    particle.setScaleXY(randomS * 0.3);

    const randomX = Math.random() * 20 - 10;
    particle.x += randomX;

    const randomY = Math.random() * 20 - 10;
    particle.y += randomY;

    const randomL = minLen + Math.random() * (maxLen - minLen);
    const randomA = 360 * Math.random();
    const randomT = getPosByAngle(randomA, randomL);
    moveItem(particle, particle.x + randomT.x, particle.y + randomT.y, showTime, () => {



    }, TWEEN.Easing.Exponential.Out);

    // scaleItem(particle, 0, 0.6, () => {
    //
    // });

    scaleItem(particle, randomS, 0.6, () => {

    }, TWEEN.Easing.Exponential.Out);

    setTimeout(() => {

      hideItem(particle, 0.4, () => {

      }, TWEEN.Easing.Cubic.In);

    }, showTime * 0.5 * 1000);


  }
}


export function RandomInt(a, b = 0) {
  let max = Math.max(a, b);
  let min = Math.min(a, b);
  return Math.floor(Math.random() * (max - min) + min);
}

export function showShapeParticle(shapeType, shapeW, shapeH, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) {


  const randomColorArr = [
    '#6996af',
    '#4da940',
    '#911b40',
  ]

  for (let i = 0; i < num; i ++) {

    let particle;
    switch(shapeType) {
      case 'rect': 
        particle = new ShapeRectNew();
        particle.setSize(shapeW, shapeH, 0);
        particle.fillColor = randomColorArr[RandomInt(0, randomColorArr.length)];
        break;
    }

    particle.x = pos.x;
    particle.y = pos.y;
    parent.addChild(particle);

    const randomR = 360 * Math.random();
    // particle.rotation = randomR;

    const randomS = 0.3 + Math.random() * 0.7;
    particle.setScaleXY(randomS * 0.3);

    const randomX = Math.random() * 20 - 10;
    particle.x += randomX;

    const randomY = Math.random() * 20 - 10;
    particle.y += randomY;

    const randomL = minLen + Math.random() * (maxLen - minLen);
    const randomA = 360 * Math.random();
    const randomT = getPosByAngle(randomA, randomL);
    moveItem(particle, particle.x + randomT.x, particle.y + randomT.y, showTime, () => {



    }, TWEEN.Easing.Exponential.Out);

    // scaleItem(particle, 0, 0.6, () => {
    //
    // });

    scaleItem(particle, randomS, 0.6, () => {

    }, TWEEN.Easing.Exponential.Out);

    setTimeout(() => {

      hideItem(particle, 0.4, () => {

      }, TWEEN.Easing.Cubic.In);

    }, showTime * 0.5 * 1000);


  }
}



export function shake(item, time = 0.5, callback = null, rate = 1) {

  if (item.shakeTween) {
    return;
  }

  item.shakeTween = true;
  const offX = 15 * item.scaleX * rate;
  const offY = 15 * item.scaleX * rate;
  const baseX = item.x;
  const baseY = item.y;
  const easing = TWEEN.Easing.Sinusoidal.InOut;


  const move4 = () => {
    moveItem(item, baseX, baseY, time / 4, () => {
      item.shakeTween = false;
      if (callback) {
        callback();
      }
    }, easing);
  };

  const move3 = () => {
    moveItem(item, baseX + offX / 4, baseY + offY / 4, time / 4, () => {
      move4();
    }, easing);
  };

  const move2 = () => {
    moveItem(item, baseX - offX / 4 * 3, baseY - offY / 4 * 3, time / 4, () => {
      move3();
    }, easing);
  };

  const move1 = () => {
    moveItem(item, baseX + offX, baseY + offY, time / 7.5, () => {
      move2();
    }, easing);
  };

  move1();

}

export function getMaxScale(item, maxW, maxH) {
  const sx = maxW / item.width;
  const sy = maxH / item.height;
  const maxS = Math.max(sx, sy);
  return maxS;
}

export function createSprite(key) {
  const image = window.curImages;
  const spr = new MySprite();
  spr.init(image.get(key));
  return spr;
}

export class MyVideo extends MySprite {

  element;

  initVideoElement(videoElement)  {

    this.element = videoElement;
    this.width = this.element.videoWidth;
    this.height = this.element.videoHeight;

    console.log('this.width: ', this.width);
    console.log('this.height: ', this.height);
    this.element.currentTime = 0.01;
  }


  drawSelf() {
    super.drawSelf();

    this.ctx.drawImage(this.element, 0, 0, this.width, this.height);
  }
}

export class InputView extends MySprite {

  element;
  callback;
  _isShowScroll = false;

  constructor(ctx=null) {
    super(ctx);
    this._createInput()
  }

  set isShowScroll(v) {
    this._isShowScroll = v;
    if (!v) {
      this.element.style.overflow = 'hidden';
    }
  }

  get isShowScroll() {
    return this._isShowScroll;
  }

  _createInput() {
    const input = document.createElement('textarea');
    input.style.resize = 'none';
    input.style.border = 'none';
    
    input.style.position = 'absolute';
    input.onblur = this.onblur.bind(this); 

    const div = document.getElementById('div_input');
    div.appendChild(input);

    this.element = input;
  }

  onblur() {

    if (this.callback) {
      this.callback(this.element.value);
    }
    this.callback = null;
    this.hide();
  }

  set text(v) {
    this.element.value = v;
  }

  show() {
    this.element.hidden = false;
    setTimeout(() => {
      this.element.focus();
    }, 1);
  }

  hide() {
    this.element.hidden = true;
  }

  setStyle(style) {
    for ( let key in style ) {
      console.log('key: ', key)
      console.log('value: ', style[key])
      this.element.style[key] = style[key];
    }
    console.log('this.element: ``````', this.element)
  }

  refreshInputStyle() {
    this.element.style.left = this.x + 'px';
    this.element.style.top = this.y + 'px';
    this.element.style.width = this.width + 'px';
    this.element.style.height = this.height + 'px';
  }

}

export class ScrollView extends MySprite {

  static ScrollSideType = {
    VERTICAL : 'VERTICAL',
    HORIZONTAL : 'HORIZONTAL',
  }

  _offCtx;
  _offCanvas;
  _scrollBar;

  content;
  bgColor;
  barColor = '#fbe9b7'
  scrollSide = ScrollView.ScrollSideType.VERTICAL;

  scorllBarWidth;
  scrollBarHeight;
  itemArr = [];

  constructor(ctx = null) {
    super(ctx);

    this._createOffCtx();
    this._createScrollBar();
  }

  _createOffCtx() {
    if (!this._offCtx) {
      this._offCanvas = document.createElement('canvas'); // 创建一个新的canvas
      this._offCanvas.width = this.width; // 创建一个正好包裹住一个粒子canvas
      this._offCanvas.height = this.height;
      this._offCtx = this._offCanvas.getContext('2d');

      this.content = new MySprite(this._offCtx);
    }
  }

  _createScrollBar() {
    this._scrollBar = new ShapeRectNew();
    this._scrollBar.anchorY = 0;
    this._scrollBar.anchorX = 1;
    this._scrollBar.setSize(10, 100, 5);
    this._scrollBar.fillColor = this.barColor;
    this.addChild(this._scrollBar);
  }
 
  setBgColor(color) {
    this.bgColor = color;
  }

  setShowSize(w, h) {

    this.width = w;
    this.height = h;

    if (this.content.width < this.width) {
      this.content.width = this._offCanvas.width = this.width;
    }
    if (this.content.height < this.height) {
      this.content.height = this._offCanvas.height = this.height;
    }

    this.refreshScrollBar();
  }
  

  setContentSize(w, h) {
    this.content.width = w;
    this.content.height = h;

    this._offCanvas.width = w;
    this._offCanvas.height = h;
  }

  addItem(sprite) {
    this.itemArr.push(sprite);
    this.content.addChild(sprite);
    sprite.ctx = this._offCtx;
    this.refreshContentSize();
  }

  refreshContentSize() {
    const children = this.itemArr;
    // console.log('children: ', children);

    
    let maxW = 0;
    let maxH = 0;
    for (let i=0; i<children.length; i++) {

      const box = children[i].getBoundingBox();
      const curChild = children[i];
      // console.log('box: ', box);
      const boxEdgeX = curChild.x + (1 - curChild.anchorX) * curChild.width * curChild.scaleX;
      const boxEdgeY = curChild.y + (1 - curChild.anchorY) * curChild.height * curChild.scaleY;

      if (!children[i].colorRect) {
        // const rect = new ShapeRectNew();
        // rect.fillColor = '#ff0000';
        // rect.setSize(curChild.width * curChild.scaleX, curChild.height * curChild.scaleY, 0);
        // rect.alpha = 0.3;
        // rect.x =  boxEdgeX - curChild.width * curChild.scaleX;
        // rect.y =  boxEdgeY - curChild.height * curChild.scaleY;

        // this.content.addChild(rect);
        // children[i].colorRect = rect;
      }
      
      // console.log('boxEdgeY: ', boxEdgeY);
      if (boxEdgeX > maxW) {
        maxW = boxEdgeX;
      }
      if (boxEdgeY > maxH) {
        maxH = boxEdgeY;
      }
    }

    // console.log('maxW: ', maxW);s
    // console.log('maxH: ', maxH);
    // console.log('this.y: ', this.y);
    // console.log(this.getBoundingBox().height);

    // const box = this.getBoundingBox();
    this.content.width = maxW;
    this.content.height = maxH + 10 //+ 500;
    

    this.refreshScrollBar();
  }

  setScrollBarSize(w, h) {
    this.scorllBarWidth = w;
    this.scrollBarHeight = h;
  }

  setContentScale(s) {
    this.content.setScaleXY( 1 / s);
    this.refreshScrollBar();
  }

  refreshScrollBar() {
    let w = this.scorllBarWidth;
    if (!w) {
      w = this.width / 50;
    }


    const rect1 = this.getBoundingBox();
    const rect2 = this.content.getBoundingBox();
    const sRate = rect1.height / this.height; 

    let rate = rect1.height / rect2.height;
    // let rate = this.height / this.content.height;
    // let rate = this.height * this.scaleY / this.content.height / this.content.scaleY;
    if (rate >= 1) {
      this._scrollBar.visible = false;
      rate = 1;
    } else {
      this._scrollBar.visible = true;
    }
    const h = rate * this.height / sRate;
    const r = w / 2;
    this._scrollBar.setSize(w, h, r);

    this._scrollBar.x = this.width;
  }

  refreshScrollBarPos() {
    const rect1 = this.getBoundingBox();
    const rect2 = this.content.getBoundingBox();
    let rate = this.height / this.content.height;

    
    this._scrollBar.y = -this.content.y * (rate );
  }


  drawSelf() {

    super.drawSelf();

    this._offScreenRender();

  }






  touchStartPos;
  touchStartContentPos;
  onTouchStart(x, y) {

    if (!this._scrollBar.visible) {
      return;
    }

    this.touchStartPos = { x, y };
    this.touchStartContentPos = { x: this.content.x, y: this.content.y };
  }

  onTouchMove(x, y) {

    if (!this.touchStartPos) {
      return;
    }


    if (!this.touchStartContentPos) {
      return;
    }


    const offsetX = x - this.touchStartPos.x;
    const offsetY = y - this.touchStartPos.y;
    const rect = this.getBoundingBox();
    const rect2 = this.content.getBoundingBox();

    if (this.scrollSide == ScrollView.ScrollSideType.VERTICAL) {
      this.content.y = between(this.touchStartContentPos.y + offsetY, 0, this.height - this.content.height);
    } else {
      this.content.x = between(this.touchStartContentPos.x + offsetX, 0, this.width  - this.content.width);
    }
    this.refreshScrollBarPos();
  }

  onTouchEnd(x, y) {
    this.touchStartPos = null;
    this.touchStartContentPos = null;
  }

  onWheelUp(offsetY) {
    if (!this._scrollBar.visible) {
      return;
    }
    const rect = this.getBoundingBox();

    if (this.scrollSide == ScrollView.ScrollSideType.VERTICAL) {
      this.content.y = between(this.content.y + 40, 0, this.height - this.content.height);
    } else {
      this.content.x = between(this.content.x + 40, 0, this.width - this.content.width);
    }
    this.refreshScrollBarPos();

  }

  onWheelDown(offsetY) {
    if (!this._scrollBar.visible) {
      return;
    }

    const rect = this.getBoundingBox();

    if (this.scrollSide == ScrollView.ScrollSideType.VERTICAL) {
      this.content.y = between(this.content.y - 40, 0,  this.height - this.content.height);
    } else {
      this.content.x = between(this.content.x - 40, 0,  this.width - this.content.width);
    }
    this.refreshScrollBarPos();

  }

  setContentSpr() {

  }



  _offScreenRender() {

    this._offCtx.save();

    this._offCtx.clearRect(0, 0, this._offCanvas.width, this._offCanvas.height);




    if (this.bgColor) {
      this._offCtx.fillStyle = this.bgColor;
      this._offCtx.fillRect(this._offX, this._offY, this.width, this.height);
      this._offCtx.globalCompositeOperation =  "source-atop";

    } else {
      this._offCtx.fillStyle = '#ffffff'
      this._offCtx.fillRect(this._offX, this._offY, this.width, this.height);
      this._offCtx.globalCompositeOperation =  "xor";

    }


    this.content.update();

    this.ctx.drawImage(this._offCanvas, this._offX, this._offY);



    


    // this.ctx.drawImage(this._offCanvas, this._offX, this._offY);

       
    // this.ctx.drawImage(this._offCanvas, this._offX, this._offY, this.content.width, this.content.height, this._offX + this.content.x, this._offY + this.content.y, this.width, this.height);


    this._offCtx.restore();

  }
 
}

export function between(a, b, c) {
  return [a, b, c].sort((x, y) => x - y)[1];
}




// --------------- custom class --------------------

