From 2aac77781548a1e66dc1d40d375b71b2378c137c Mon Sep 17 00:00:00 2001
From: lmz <limingzhe@iplayabc.com>
Date: Mon, 22 Jun 2020 09:37:23 +0800
Subject: [PATCH] fix: custom-hot-zone

---
 src/app/common/custom-hot-zone/Unit.ts        | 2869 +++++++++++++++--
 .../custom-hot-zone.component.html            |   63 +-
 .../custom-hot-zone.component.scss            |    7 +
 .../custom-hot-zone.component.ts              |  401 ++-
 tsconfig.app.json                             |    4 +-
 5 files changed, 2940 insertions(+), 404 deletions(-)

diff --git a/src/app/common/custom-hot-zone/Unit.ts b/src/app/common/custom-hot-zone/Unit.ts
index 5823ac0..7b99189 100644
--- a/src/app/common/custom-hot-zone/Unit.ts
+++ b/src/app/common/custom-hot-zone/Unit.ts
@@ -1,6 +1,11 @@
 
 import TWEEN from '@tweenjs/tween.js';
 
+interface AirWindow  extends Window {
+  air: any;
+  curCtx: any;
+}
+declare let window: AirWindow;
 
 class Sprite {
   x = 0;
@@ -12,8 +17,12 @@ class Sprite {
   angle = 0;
   ctx;
 
-  constructor(ctx) {
-    this.ctx = ctx;
+  constructor(ctx = null) {
+    if (!ctx) {
+      this.ctx = window.curCtx;
+    } else {
+      this.ctx = ctx;
+    }
   }
   update($event) {
     this.draw();
@@ -30,25 +39,45 @@ class Sprite {
 
 export class MySprite extends Sprite {
 
-  width = 0;
-  height = 0;
+  _width = 0;
+  _height = 0;
   _anchorX = 0;
   _anchorY = 0;
   _offX = 0;
   _offY = 0;
   scaleX = 1;
   scaleY = 1;
-  alpha = 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;
 
-  init(imgObj = null, anchorX:number = 0.5, anchorY:number = 0.5) {
+
+  init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {
 
     if (imgObj) {
 
@@ -56,6 +85,8 @@ export class MySprite extends Sprite {
 
       this.width = this.img.width;
       this.height = this.img.height;
+
+
     }
 
     this.anchorX = anchorX;
@@ -63,11 +94,35 @@ export class MySprite extends Sprite {
   }
 
 
+  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;
+  }
+
+
+
 
   update($event = null) {
-    if (this.visible) {
-      this.draw();
+    if (!this.visible && this.childDepandVisible) {
+      return;
     }
+
+    this.draw();
+
   }
   draw() {
 
@@ -78,6 +133,8 @@ export class MySprite extends Sprite {
     this.updateChildren();
 
     this.ctx.restore();
+
+
   }
 
   drawInit() {
@@ -90,26 +147,73 @@ export class MySprite extends Sprite {
 
     this.ctx.globalAlpha = this.alpha;
 
+    this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0);
+
+
+
+
+    //
+    // if (this._radius) {
+    //
+    //   const r = this._radius;
+    //   const w = this.width;
+    //   const h = this.height;
+    //
+    //   this.ctx.lineTo(-w / 2, h / 2);          // 创建水平线
+    //   this.ctx.arcTo(-w / 2, -h / 2, -w / 2 + r, -h / 2, r);
+    //   this.ctx.arcTo(w / 2, -h / 2, w / 2, -h / 2 + r, r);
+    //   this.ctx.arcTo(w / 2, h / 2, w / 2 - r, h / 2, r);
+    //   this.ctx.arcTo(-w / 2, h / 2, -w / 2, h / 2 - r, r);
+    //
+    //   this.ctx.clip();
+    // }
+
   }
 
   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.img) {
-      this.ctx.drawImage(this.img, this._offX, this._offY);
+
+      if (this._showRect) {
+        const rect = this._showRect;
+        this.ctx.drawImage(this.img, rect.x, rect.y, rect.width, rect.height, this._offX, this._offY + rect.y, this.width, rect.height);
+      } else {
+        this.ctx.drawImage(this.img, this._offX, this._offY);
+      }
     }
+
+
+
+
   }
 
   updateChildren() {
 
     if (this.children.length <= 0) { return; }
 
-    for (let i = 0; i < this.children.length; i++) {
-
-      if (this.children[i] === this) {
-
-        this.drawSelf();
+    for (const child of  this.children) {
+      if (child === this) {
+        if (this.visible) {
+          this.drawSelf();
+        }
       } else {
-
-        this.children[i].update();
+        child.update();
       }
     }
   }
@@ -140,6 +244,11 @@ export class MySprite extends Sprite {
       return a._z - b._z;
     });
 
+
+    if (this.childDepandAlpha) {
+      child.alpha = this.alpha;
+    }
+
   }
   removeChild(child) {
     const index = this.children.indexOf(child);
@@ -148,229 +257,1715 @@ export class MySprite extends Sprite {
     }
   }
 
-  set anchorX(value) {
-    this._anchorX = value;
-    this.refreshAnchorOff();
+  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 btimapFlag(v) {
+    this._bitmapFlag = v;
+  }
+  get btimapFlag() {
+    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 RoundSprite extends MySprite {
+
+  _newCtx;
+
+  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.anchorX = anchorX;
+    this.anchorY = anchorY;
+
+    const canvas = window['curCanvas'];
+    const w = canvas.nativeElement.width;
+    const h = canvas.nativeElement.height;
+
+
+    this._offCanvas = document.createElement('canvas');
+    this._offCanvas.width = w;
+    this._offCanvas.height = h;
+
+    this._offCtx = this._offCanvas.getContext('2d');
+
+    // this._newCtx = this.ctx;
+    // this.ctx = this._offCtx;
+
+  }
+
+
+
+  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;
+      const x = -this._offX;
+      const y = -this._offY;
+
+      this._offCtx.lineTo(x - w / 2, y + h / 2);          // 创建水平线
+      this._offCtx.arcTo(x - w / 2, y - h / 2, x - w / 2 + r, y - h / 2, r);
+      this._offCtx.arcTo(x + w / 2, y - h / 2, x + w / 2, y - h / 2 + r, r);
+      this._offCtx.arcTo(x + w / 2, y + h / 2, x + w / 2 - r, y + h / 2, r);
+      this._offCtx.arcTo(x - w / 2, y + h / 2, x - w / 2, y + h / 2 - r, r);
+
+      this._offCtx.clip();
+    }
+
+
+    if (this.img) {
+
+      this._offCtx.drawImage(this.img, 0, 0);
+      this.ctx.drawImage(this._offCanvas,this._offX, this._offX);
+    }
+
+  }
+}
+
+
+
+
+
+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 {
+  private _text: string;
+
+  // fontSize:String = '40px';
+  fontName = 'Verdana';
+  textAlign = 'left';
+  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();
+  }
+
+
+  get text(): string {
+    return this._text;
+  }
+
+  set text(value: string) {
+    this._text = value;
+    this.refreshSize();
+  }
+
+  refreshSize() {
+
+    this.ctx.save();
+
+    this.ctx.font = `${this.fontSize * this.scaleX}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    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.
+  }
+
+  setOutline(width = 5, color = '#ffffff') {
+
+    this._outlineFlag = true;
+    this._outLineWidth = width;
+    this._outLineColor = color;
+
+  }
+
+  drawText() {
+
+
+    if (!this.text) { return; }
+
+
+    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 {
+
+
+  disH =  30;
+
+  constructor(ctx?: any) {
+    super(ctx);
+
+    // this.dataArr = dataArr;
+  }
+
+
+
+  drawText() {
+
+    if (!this.text) {
+      return;
+    }
+
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+    this.ctx.fillStyle = this.fontColor;
+
+
+    const selfW = this.width * this.scaleX;
+
+
+    const chr = this.text.split(' ');
+    let temp = '';
+    const row = [];
+    const w = selfW - 80;
+    const disH = (this.fontSize + this.disH) * this.scaleY;
+
+
+
+
+    for (const c of  chr) {
+      if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (c)).width <= w) {
+        temp += ' ' + c;
+      } else {
+        row.push(temp);
+        temp = ' ' + c;
+      }
+    }
+    row.push(temp);
+
+    const x = 0;
+    const y = -row.length * disH / 2;
+    // for (let b = 0 ; b < row.length; b++) {
+    //   this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+    // }
+
+
+    if (this._outlineFlag) {
+      this.ctx.lineWidth = this._outLineWidth;
+      this.ctx.strokeStyle = this._outLineColor;
+      for (let b = 0 ; b < row.length; b++) {
+        this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+      }
+      // this.ctx.strokeText(this.text, 0, 0);
+    }
+
+
+    // this.ctx.fillStyle = '#ff7600';
+    for (let b = 0 ; b < row.length; b++) {
+      this.ctx.fillText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+    }
+
+  }
+
+
+  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() {
+
+    this.ctx.fillStyle = this.fillColor;
+    this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawShape();
+  }
+}
+
+
+
+export class Line extends MySprite {
+
+
+  lineWidth = 5;
+  lineColor = '#000000';
+  _pointArr = [];
+
+  roundFlag = true;
+
+  _pointS = 1;
+
+
+  imgObj;
+  bitMap;
+  _offCtx;
+  _offCanvas;
+
+  lastPointIndex = 0;
+
+  init() {
+
+    const canvas = window['curCanvas'];
+    const w = canvas.nativeElement.width;
+    const h = canvas.nativeElement.height;
+
+    console.log('w: ', w);
+    console.log('h: ', h);
+
+
+    this._offCanvas = document.createElement('canvas');
+    this._offCanvas.width = w;
+    this._offCanvas.height = h;
+    // this._offCanvas = _offCanvas;
+    // this._offCtx = this._offCanvas.getContext('2d');
+
+
+    // this._offCanvas = new OffscreenCanvas(w, h);
+    this._offCtx = this._offCanvas.getContext('2d');
+  }
+
+  addPoint(x, y) {
+
+    this._pointArr.push([x, y]);
+
+    if (this._pointArr.length < 2) {
+      return;
+    }
+    //
+    // const lastP = this._pointArr[this._pointArr.length - 1];
+    //
+    //
+    // const context = this._offCtx;
+    // context.moveTo (lastP[0], lastP[1]);       // 设置起点状态
+    // context.lineTo (x, y);       // 设置末端状态
+    //
+    // context.lineWidth = this.lineWidth;          //设置线宽状态
+    // context.strokeStyle = this.lineColor;
+    // context.stroke();
+    //
+    //
+    // this.bitMap = this._offCanvas.transferToImageBitmap();
+
+
+
+    // const tmpLine = new MySprite(this._offCtx);
+    // tmpLine.init(this.imgObj);
+    // tmpLine.anchorY = 1;
+    // tmpLine.anchorX = 0.5;
+    // tmpLine.x = lastP[0];
+    // tmpLine.y = lastP[1];
+    //
+    // const disH = getPosDistance(lastP[0], lastP[1], x, y);
+    // tmpLine.scaleX = this.lineWidth / tmpLine.width;
+    // tmpLine.scaleY = disH / tmpLine.height * 1.1;
+    //
+    // const angle = getAngleByPos(lastP[0], lastP[1], x, y);
+    // tmpLine.rotation = angle;
+    //
+    // this.addChild(tmpLine);
+
+  }
+
+  setPointArr(arr, imgObj) {
+
+    this.removeChildren();
+
+    if (arr.length < 2) {
+      return;
+    }
+
+    let p1 = arr[0];
+    let p2;
+    for (let i = 1; i < arr.length; i++) {
+
+      p2 = arr[i];
+
+      const tmpLine = new MySprite();
+      tmpLine.init(imgObj);
+      tmpLine.anchorY = 1;
+      tmpLine.anchorX = 0.5;
+      tmpLine.x = p1[0];
+      tmpLine.y = p1[1];
+
+      const disH = getPosDistance(p1[0], p1[1], p2[0], p2[1]);
+      tmpLine.scaleX = this.lineWidth / tmpLine.width;
+      tmpLine.scaleY = disH / tmpLine.height * 1.1;
+
+      const angle = getAngleByPos(p1[0], p1[1], p2[0], p2[1]);
+      tmpLine.rotation = angle;
+
+      this.addChild(tmpLine);
+
+      p1 = p2;
+    }
+  }
+
+
+
+
+  drawLine() {
+
+    if (this._pointArr.length < 2) {
+      return;
+    }
+
+
+    const curMaxPointIndex = this._pointArr.length - 1;
+    if (curMaxPointIndex > this.lastPointIndex) {
+
+      const arr = this._pointArr;
+
+      const context = this._offCtx;
+      context.moveTo (arr[this.lastPointIndex][0] * this._pointS, arr[this.lastPointIndex][1] * this._pointS);       // 设置起点状态
+      for (let i = this.lastPointIndex + 1; i < arr.length; i++) {
+        context.lineTo (arr[i][0] * this._pointS, arr[i][1] * this._pointS);       // 设置末端状态
+      }
+
+      if (this.roundFlag) {
+        context.lineCap = "round";
+      }
+      context.lineWidth = this.lineWidth;          //设置线宽状态
+      context.strokeStyle = this.lineColor;
+      context.stroke();
+
+
+      this.lastPointIndex = curMaxPointIndex;
+
+      // this.bitMap = this._offCanvas.transferToImageBitmap();
+
+    }
+
+
+    // this.ctx.drawImage(this.bitMap, this._offX, this._offY);
+    this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
+
+  }
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawLine();
+
+
+    // if (this.img) {
+    //   this.ctx.drawImage(this._offCanvas, 0, 0, this.width, this.height);
+    // }
+
+    // if (this.bitMap) {
+    //   this.bitMap = this._offCanvas.transferToImageBitmap();
+    //   this.ctx.drawImage(this.bitMap, 0, 0, this.width, this.height);
+    // }
+
+  }
+}
+
+
+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, height - radius, radius, 0, Math.PI / 2);
+
+    // 矩形下边线
+    ctx.lineTo(radius, height);
+
+    // 左下角圆弧,弧度从1/2PI到PI
+    ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
+
+    // 矩形左边线
+    ctx.lineTo(0, radius);
+
+    // 左上角圆弧,弧度从PI到3/2PI
+    ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
+
+    // 上边线
+    ctx.lineTo(width - radius, 0);
+
+    // 右上角圆弧
+    ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
+
+    // 右边线
+    ctx.lineTo(width, height - radius);
+    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 = 1;
+
+  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) {
+      this.playEndFunc();
+      this.playEndFunc = null;
+    }
+  }
+
+  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 = 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);
+    });
+  }
+
+  tween.start();
+  return tween;
+}
+
+
+
+
+export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = null) {
+
+  const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  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) {
+  if (!arr) {
+    return;
+  }
+  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;
   }
-  get anchorX() {
-    return this._anchorX;
+  if (mx === px && my > py) {// 鼠标在y轴负方向上
+    angle = 180;
   }
-  set anchorY(value) {
-    this._anchorY = value;
-    this.refreshAnchorOff();
+  if (mx > px && my === py) {// 鼠标在x轴正方向上
+    angle = 90;
   }
-  get anchorY() {
-    return this._anchorY;
+  if (mx < px && my > py) {// 鼠标在第三象限
+    angle = 180 + angle;
   }
-  refreshAnchorOff() {
-    this._offX = -this.width * this.anchorX;
-    this._offY = -this.height * this.anchorY;
+  if (mx < px && my === py) {// 鼠标在x轴负方向
+    angle = 270;
   }
-
-  setScaleXY(value) {
-    this.scaleX = this.scaleY = value;
+  if (mx < px && my < py) {// 鼠标在第二象限
+    angle = 360 - angle;
   }
 
-  getBoundingBox() {
+  // console.log('angle: ', angle);
+  return angle;
 
-    const x = this.x + this._offX * this.scaleX;
-    const y = this.y + this._offY * this.scaleY;
-    const width = this.width * this.scaleX;
-    const height = this.height * this.scaleY;
+}
 
-    return {x, y, width, height};
-  }
 
+export function removeItemFromArr(arr, item) {
+  const index = arr.indexOf(item);
+  if (index !== -1) {
+    arr.splice(index, 1);
+  }
 }
 
 
 
 
 
-export class Item extends MySprite {
 
-  baseX;
+export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer = 1, callBack = null, easing = null) {
 
-  move(targetY, callBack) {
+  const r = getPosDistance(item.x, item.y, x0, y0);
+  let a = getAngleByPos(item.x, item.y, x0, y0);
+  a += 90;
+  const obj = {r, a};
 
-    const self = this;
+  item._circleAngle = a;
+  const targetA = a + addR;
 
-    const tween = new TWEEN.Tween(this)
-      .to({ y: targetY }, 2500)
-      .easing(TWEEN.Easing.Quintic.Out)
-      .onComplete(function() {
-
-        self.hide(callBack);
-        // if (callBack) {
-        //   callBack();
-        // }
-      })
-      .start();
+  const tween = new TWEEN.Tween(item).to({_circleAngle: targetA}, time * 1000);
 
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
   }
 
-  show(callBack = null) {
+  tween.onUpdate( (item, progress) => {
 
-    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(function() {
-        if (callBack) {
-          callBack();
-        }
-      })
-      .start(); // Start the tween immediately.
+    // 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);
 
-  hide(callBack = null) {
+    item.x = x;
+    item.y = y;
 
-    const tween = new TWEEN.Tween(this)
-      .to({ alpha: 0 }, 800)
-      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
-      .onComplete(function() {
-        if (callBack) {
-          callBack();
-        }
-      })
-      .start(); // Start the tween immediately.
-  }
+    // obj.a ++;
+  });
 
+  tween.start();
+}
 
-  shake(id) {
 
+export function getPosDistance(sx, sy, ex, ey) {
 
-    if (!this.baseX) {
-      this.baseX = this.x;
-    }
+  const _x = ex - sx;
+  const _y = ey - sy;
+  const len = Math.sqrt( Math.pow(_x, 2) + Math.pow(_y, 2) );
+  return len;
+}
 
-    const baseX = this.baseX;
-    const baseTime = 50;
-    const sequence = [
-      {target: {x: baseX + 40 * id}, time: baseTime - 25},
-      {target: {x: baseX - 20 * id}, time: baseTime},
-      {target: {x: baseX + 10 * id}, time: baseTime},
-      {target: {x: baseX - 5 * id}, time: baseTime},
-      {target: {x: baseX + 2 * id}, time: baseTime},
-      {target: {x: baseX - 1 * id}, time: baseTime},
-      {target: {x: baseX}, time: baseTime},
+export function delayCall(callback, second) {
+  const tween = new TWEEN.Tween(this)
+    .delay(second * 1000)
+    .onComplete(() => {
+      if (callback) {
+        callback();
+      }
+    })
+    .start();
+}
 
-    ];
 
+export function formatTime(fmt, date) {
 
-    const self = this;
+  // "yyyy-MM-dd HH:mm:ss";
 
-    function runSequence() {
+  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;
+}
 
-      if (self['shakeTween']) {
-        self['shakeTween'].stop();
-      }
+export function getMinScale(item, maxLen) {
+  const sx = maxLen / item.width;
+  const sy = maxLen / item.height;
+  const minS = Math.min(sx, sy);
+  return minS;
+}
 
-      const tween = new TWEEN.Tween(self);
 
-      if (sequence.length > 0) {
-        // console.log('sequence.length: ', sequence.length);
-        const action = sequence.shift();
-        tween.to(action['target'], action['time']);
-        tween.onComplete( () => {
-          runSequence();
-        });
-        tween.start();
 
-        self['shakeTween'] = tween;
-      }
-    }
+export function jelly(item, time = 0.7) {
 
-    runSequence();
 
+  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();
+}
+
 
 
-  drop(targetY, callBack = null) {
+export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) {
 
-    const self = this;
 
-    const time = Math.abs(targetY - this.y) * 2.4;
+  for (let i = 0; i < num; i ++) {
 
-    this.alpha = 1;
+    const particle = new MySprite();
+    particle.init(img);
+    particle.x = pos.x;
+    particle.y = pos.y;
+    parent.addChild(particle);
 
-    const tween = new TWEEN.Tween(this)
-      .to({ y: targetY }, time)
-      .easing(TWEEN.Easing.Cubic.In)
-      .onComplete(function() {
+    const randomR = 360 * Math.random();
+    particle.rotation = randomR;
 
-        // self.hideItem(callBack);
-        if (callBack) {
-          callBack();
-        }
-      })
-      .start();
+    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, () => {
 
-}
 
 
-export class EndSpr extends MySprite {
+    }, TWEEN.Easing.Exponential.Out);
 
-  show(s) {
+    // scaleItem(particle, 0, 0.6, () => {
+    //
+    // });
 
-    this.scaleX = this.scaleY = 0;
-    this.alpha = 0;
+    scaleItem(particle, randomS, 0.6, () => {
 
-    const tween = new TWEEN.Tween(this)
-      .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
-      .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
-      .onComplete(function() {
+    }, TWEEN.Easing.Exponential.Out);
+
+    setTimeout(() => {
+
+      hideItem(particle, 0.4, () => {
+
+      }, TWEEN.Easing.Cubic.In);
+
+    }, showTime * 0.5 * 1000);
 
-      })
-      .start(); // Start the tween immediately.
 
   }
 }
 
 
 
-export class ShapeRect extends MySprite {
+export function shake(item, time = 0.5, callback = null, rate = 1) {
 
-  fillColor = '#FF0000';
+  if (item.shakeTween) {
+    return;
+  }
 
-  setSize(w, h) {
-    this.width = w;
-    this.height = h;
+  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;
 
-    console.log('w:', w);
-    console.log('h:', h);
-  }
 
-  drawShape() {
+  const move4 = () => {
+    moveItem(item, baseX, baseY, time / 4, () => {
+      item.shakeTween = false;
+      if (callback) {
+        callback();
+      }
+    }, easing);
+  };
 
-    this.ctx.fillStyle = this.fillColor;
-    this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
+  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();
 
-  drawSelf() {
-    super.drawSelf();
-    this.drawShape();
-  }
 }
 
+// --------------- custom class --------------------
+
+
 
 export class HotZoneItem extends MySprite {
 
+
   lineDashFlag = false;
   arrow: MySprite;
   label: Label;
-  text;
+  title;
 
   arrowTop;
   arrowRight;
 
+  audio_url;
+  pic_url;
+  text;
+  private _itemType;
+  private shapeRect: ShapeRect;
+
+  get itemType() {
+    return this._itemType;
+  }
+  set itemType(value) {
+    this._itemType = value;
+  }
+
   setSize(w, h) {
     this.width = w;
     this.height = h;
@@ -391,7 +1986,7 @@ export class HotZoneItem extends MySprite {
     if (!this.label) {
       this.label = new Label(this.ctx);
       this.label.anchorY = 0;
-      this.label.fontSize = '40px';
+      this.label.fontSize = 40;
       this.label.textAlign = 'center';
       this.addChild(this.label);
       // this.label.scaleX = 1 / this.scaleX;
@@ -403,8 +1998,8 @@ export class HotZoneItem extends MySprite {
 
     if (text) {
       this.label.text = text;
-    } else if (this.text) {
-      this.label.text = this.text;
+    } else if (this.title) {
+      this.label.text = this.title;
     }
     this.label.visible = true;
 
@@ -532,12 +2127,86 @@ export class HotZoneItem extends MySprite {
   }
 }
 
+export class HotZoneImg extends MySprite {
+
+  drawFrame() {
+
+    this.ctx.save();
+
+    const rect = this.getBoundingBox();
+    const w = rect.width;
+    const h = rect.height;
+    const x = rect.x + w / 2;
+    const y = rect.y + h / 2;
+
+    this.ctx.setLineDash([5, 5]);
+    this.ctx.lineWidth = 2;
+    this.ctx.strokeStyle = '#1bfff7';
+
+    this.ctx.beginPath();
+    this.ctx.moveTo( x - w / 2, y - h / 2);
+    this.ctx.lineTo(x + w / 2, y - h / 2);
+    this.ctx.lineTo(x + w / 2, y + h / 2);
+    this.ctx.lineTo(x - w / 2, y + h / 2);
+    this.ctx.lineTo(x - w / 2, y - h / 2);
+
+    // this.ctx.fill();
+    this.ctx.stroke();
+
+    this.ctx.restore();
+  }
+
+  draw() {
+    super.draw();
+
+    this.drawFrame();
+  }
+}
+
+export class HotZoneLabel extends Label {
+
+  drawFrame() {
+
+    this.ctx.save();
+
+    const rect = this.getBoundingBox();
+    const w = rect.width / this.scaleX;
+    const h = this.height * this.scaleY;
+    const x = this.x;
+    const y = this.y;
+
+    this.ctx.setLineDash([5, 5]);
+    this.ctx.lineWidth = 2;
+    this.ctx.strokeStyle = '#1bfff7';
+
+    this.ctx.beginPath();
+    this.ctx.moveTo( x - w / 2, y - h / 2);
+    this.ctx.lineTo(x + w / 2, y - h / 2);
+    this.ctx.lineTo(x + w / 2, y + h / 2);
+    this.ctx.lineTo(x - w / 2, y + h / 2);
+    this.ctx.lineTo(x - w / 2, y - h / 2);
+
+    // this.ctx.fill();
+    this.ctx.stroke();
+
+    this.ctx.restore();
+  }
+
+  draw() {
+    super.draw();
+
+    this.drawFrame();
+  }
+}
+
+
+
 
 export class EditorItem extends MySprite {
 
   lineDashFlag = false;
   arrow: MySprite;
-  label:Label;
+  label: Label;
   text;
 
   showLabel(text = null) {
@@ -546,7 +2215,7 @@ export class EditorItem extends MySprite {
     if (!this.label) {
       this.label = new Label(this.ctx);
       this.label.anchorY = 0;
-      this.label.fontSize = '50px';
+      this.label.fontSize = 50;
       this.label.textAlign = 'center';
       this.addChild(this.label);
       this.label.setScaleXY(1 / this.scaleX);
@@ -657,109 +2326,783 @@ export class EditorItem extends MySprite {
 
 
 
-export class Label extends MySprite {
-
-  text:String;
-  fontSize:String = '40px';
-  fontName:String = 'Verdana';
-  textAlign:String = 'left';
-
-
-  constructor(ctx) {
-    super(ctx);
-    this.init();
-  }
-
-  drawText() {
-
-    // console.log('in drawText', this.text);
-
-    if (!this.text) { return; }
-
-    this.ctx.font = `${this.fontSize} ${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);
-
-
-  }
-
-
-  drawSelf() {
-    super.drawSelf();
-    this.drawText();
-  }
-
-}
-
-
-
-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 = p2x - p1x;
-  // const _y = p2y - p1y;
-  // const tan = _y / _x;
-  //
-  // const radina = Math.atan(tan); // 用反三角函数求弧度
-  // const angle = Math.floor(180 / (Math.PI / radina)); //
-  //
-  // console.log('r: ' , angle);
-  // return angle;
-  //
-
-
-
-  const x = Math.abs(px - mx);
-  const y = Math.abs(py - my);
-  // const x = Math.abs(mx - px);
-  // const y = Math.abs(my - py);
-  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;
-
-}
+//
+//
+// import TWEEN from '@tweenjs/tween.js';
+//
+//
+// class Sprite {
+//   x = 0;
+//   y = 0;
+//   color = '';
+//   radius = 0;
+//   alive = false;
+//   margin = 0;
+//   angle = 0;
+//   ctx;
+//
+//   constructor(ctx) {
+//     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;
+//
+//   children = [this];
+//
+//   img;
+//   _z = 0;
+//
+//
+//   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.anchorX = anchorX;
+//     this.anchorY = anchorY;
+//   }
+//
+//
+//
+//   update($event = null) {
+//     if (this.visible) {
+//       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;
+//
+//   }
+//
+//   drawSelf() {
+//     if (this.img) {
+//       this.ctx.drawImage(this.img, this._offX, this._offY);
+//     }
+//   }
+//
+//   updateChildren() {
+//
+//     if (this.children.length <= 0) { return; }
+//
+//     for (let i = 0; i < this.children.length; i++) {
+//
+//       if (this.children[i] === this) {
+//
+//         this.drawSelf();
+//       } else {
+//
+//         this.children[i].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;
+//     });
+//
+//   }
+//   removeChild(child) {
+//     const index = this.children.indexOf(child);
+//     if (index !== -1) {
+//       this.children.splice(index, 1);
+//     }
+//   }
+//
+//   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 x = this.x + this._offX * this.scaleX;
+//     const y = this.y + this._offY * this.scaleY;
+//     const width = this.width * this.scaleX;
+//     const height = this.height * this.scaleY;
+//
+//     return {x, y, width, height};
+//   }
+//
+// }
+//
+//
+//
+//
+//
+// export class Item extends MySprite {
+//
+//   baseX;
+//
+//   move(targetY, callBack) {
+//
+//     const self = this;
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ y: targetY }, 2500)
+//       .easing(TWEEN.Easing.Quintic.Out)
+//       .onComplete(function() {
+//
+//         self.hide(callBack);
+//         // if (callBack) {
+//         //   callBack();
+//         // }
+//       })
+//       .start();
+//
+//   }
+//
+//   show(callBack = null) {
+//
+//     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(function() {
+//         if (callBack) {
+//           callBack();
+//         }
+//       })
+//       .start(); // Start the tween immediately.
+//
+//   }
+//
+//   hide(callBack = null) {
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ alpha: 0 }, 800)
+//       // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+//       .onComplete(function() {
+//         if (callBack) {
+//           callBack();
+//         }
+//       })
+//       .start(); // Start the tween immediately.
+//   }
+//
+//
+//   shake(id) {
+//
+//
+//     if (!this.baseX) {
+//       this.baseX = this.x;
+//     }
+//
+//     const baseX = this.baseX;
+//     const baseTime = 50;
+//     const sequence = [
+//       {target: {x: baseX + 40 * id}, time: baseTime - 25},
+//       {target: {x: baseX - 20 * id}, time: baseTime},
+//       {target: {x: baseX + 10 * id}, time: baseTime},
+//       {target: {x: baseX - 5 * id}, time: baseTime},
+//       {target: {x: baseX + 2 * id}, time: baseTime},
+//       {target: {x: baseX - 1 * id}, time: baseTime},
+//       {target: {x: baseX}, time: baseTime},
+//
+//     ];
+//
+//
+//     const self = this;
+//
+//     function runSequence() {
+//
+//       if (self['shakeTween']) {
+//         self['shakeTween'].stop();
+//       }
+//
+//       const tween = new TWEEN.Tween(self);
+//
+//       if (sequence.length > 0) {
+//         // console.log('sequence.length: ', sequence.length);
+//         const action = sequence.shift();
+//         tween.to(action['target'], action['time']);
+//         tween.onComplete( () => {
+//           runSequence();
+//         });
+//         tween.start();
+//
+//         self['shakeTween'] = tween;
+//       }
+//     }
+//
+//     runSequence();
+//
+//   }
+//
+//
+//
+//   drop(targetY, callBack = null) {
+//
+//     const self = this;
+//
+//     const time = Math.abs(targetY - this.y) * 2.4;
+//
+//     this.alpha = 1;
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ y: targetY }, time)
+//       .easing(TWEEN.Easing.Cubic.In)
+//       .onComplete(function() {
+//
+//         // self.hideItem(callBack);
+//         if (callBack) {
+//           callBack();
+//         }
+//       })
+//       .start();
+//
+//
+//   }
+//
+//
+// }
+//
+//
+// export class EndSpr extends MySprite {
+//
+//   show(s) {
+//
+//     this.scaleX = this.scaleY = 0;
+//     this.alpha = 0;
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
+//       .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
+//       .onComplete(function() {
+//
+//       })
+//       .start(); // Start the tween immediately.
+//
+//   }
+// }
+//
+//
+//
+// 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() {
+//
+//     this.ctx.fillStyle = this.fillColor;
+//     this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
+//
+//   }
+//
+//
+//   drawSelf() {
+//     super.drawSelf();
+//     this.drawShape();
+//   }
+// }
+//
+//
+// export class HotZoneItem extends MySprite {
+//
+//
+//   lineDashFlag = false;
+//   arrow: MySprite;
+//   label: Label;
+//   title;
+//
+//   arrowTop;
+//   arrowRight;
+//
+//   audio_url;
+//   pic_url;
+//   text;
+//   private _itemType;
+//   private shapeRect: ShapeRect;
+//
+//   get itemType() {
+//     return this._itemType;
+//   }
+//   set itemType(value) {
+//     this._itemType = value;
+//   }
+//
+//   setSize(w, h) {
+//     this.width = w;
+//     this.height = h;
+//
+//
+//     const rect = new ShapeRect(this.ctx);
+//     rect.x = -w / 2;
+//     rect.y = -h / 2;
+//     rect.setSize(w, h);
+//     rect.fillColor = '#ffffff';
+//     rect.alpha = 0.2;
+//     this.addChild(rect);
+//   }
+//
+//   showLabel(text = null) {
+//
+//
+//     if (!this.label) {
+//       this.label = new Label(this.ctx);
+//       this.label.anchorY = 0;
+//       this.label.fontSize = '40px';
+//       this.label.textAlign = 'center';
+//       this.addChild(this.label);
+//       // this.label.scaleX = 1 / this.scaleX;
+//       // this.label.scaleY = 1 / this.scaleY;
+//
+//       this.refreshLabelScale();
+//
+//     }
+//
+//     if (text) {
+//       this.label.text = text;
+//     } else if (this.title) {
+//       this.label.text = this.title;
+//     }
+//     this.label.visible = true;
+//
+//   }
+//
+//   hideLabel() {
+//     if (!this.label) { return; }
+//
+//     this.label.visible = false;
+//   }
+//
+//   refreshLabelScale() {
+//     if (this.scaleX == this.scaleY) {
+//       this.label.setScaleXY(1);
+//     }
+//
+//     if (this.scaleX > this.scaleY) {
+//       this.label.scaleX = this.scaleY / this.scaleX;
+//     } else {
+//       this.label.scaleY = this.scaleX / this.scaleY;
+//     }
+//   }
+//
+//   showLineDash() {
+//     this.lineDashFlag = true;
+//
+//     if (this.arrow) {
+//       this.arrow.visible = true;
+//     } else {
+//       this.arrow = new MySprite(this.ctx);
+//       this.arrow.load('assets/common/arrow.png', 1, 0);
+//       this.arrow.setScaleXY(0.06);
+//
+//       this.arrowTop = new MySprite(this.ctx);
+//       this.arrowTop.load('assets/common/arrow_top.png', 0.5, 0);
+//       this.arrowTop.setScaleXY(0.06);
+//
+//       this.arrowRight = new MySprite(this.ctx);
+//       this.arrowRight.load('assets/common/arrow_right.png', 1, 0.5);
+//       this.arrowRight.setScaleXY(0.06);
+//     }
+//
+//     this.showLabel();
+//   }
+//
+//   hideLineDash() {
+//
+//     this.lineDashFlag = false;
+//
+//     if (this.arrow) {
+//       this.arrow.visible = false;
+//     }
+//
+//     this.hideLabel();
+//   }
+//
+//
+//
+//   drawArrow() {
+//     if (!this.arrow) { return; }
+//
+//     const rect = this.getBoundingBox();
+//     this.arrow.x = rect.x + rect.width;
+//     this.arrow.y = rect.y;
+//
+//     this.arrow.update();
+//
+//
+//     this.arrowTop.x = rect.x + rect.width / 2;
+//     this.arrowTop.y = rect.y;
+//     this.arrowTop.update();
+//
+//     this.arrowRight.x = rect.x + rect.width;
+//     this.arrowRight.y = rect.y + rect.height / 2;
+//     this.arrowRight.update();
+//   }
+//
+//   drawFrame() {
+//
+//
+//     this.ctx.save();
+//
+//
+//     const rect = this.getBoundingBox();
+//
+//     const w = rect.width;
+//     const h = rect.height;
+//     const x = rect.x + w / 2;
+//     const y = rect.y + h / 2;
+//
+//     this.ctx.setLineDash([5, 5]);
+//     this.ctx.lineWidth = 2;
+//     this.ctx.strokeStyle = '#1bfff7';
+//     // this.ctx.fillStyle = '#ffffff';
+//
+//     this.ctx.beginPath();
+//
+//     this.ctx.moveTo( x - w / 2, y - h / 2);
+//
+//     this.ctx.lineTo(x + w / 2, y - h / 2);
+//
+//     this.ctx.lineTo(x + w / 2, y + h / 2);
+//
+//     this.ctx.lineTo(x - w / 2, y + h / 2);
+//
+//     this.ctx.lineTo(x - w / 2, y - h / 2);
+//
+//     // this.ctx.fill();
+//     this.ctx.stroke();
+//
+//
+//
+//
+//     this.ctx.restore();
+//
+//   }
+//
+//   draw() {
+//     super.draw();
+//
+//     if (this.lineDashFlag) {
+//       this.drawFrame();
+//       this.drawArrow();
+//     }
+//   }
+// }
+//
+//
+// export class EditorItem extends MySprite {
+//
+//   lineDashFlag = false;
+//   arrow: MySprite;
+//   label:Label;
+//   text;
+//
+//   showLabel(text = null) {
+//
+//
+//     if (!this.label) {
+//       this.label = new Label(this.ctx);
+//       this.label.anchorY = 0;
+//       this.label.fontSize = '50px';
+//       this.label.textAlign = 'center';
+//       this.addChild(this.label);
+//       this.label.setScaleXY(1 / this.scaleX);
+//     }
+//
+//     if (text) {
+//       this.label.text = text;
+//     } else if (this.text) {
+//       this.label.text = this.text;
+//     }
+//     this.label.visible = true;
+//
+//   }
+//
+//   hideLabel() {
+//     if (!this.label) { return; }
+//
+//     this.label.visible = false;
+//   }
+//
+//   showLineDash() {
+//     this.lineDashFlag = true;
+//
+//     if (this.arrow) {
+//       this.arrow.visible = true;
+//     } else {
+//       this.arrow = new MySprite(this.ctx);
+//       this.arrow.load('assets/common/arrow.png', 1, 0);
+//       this.arrow.setScaleXY(0.06);
+//
+//     }
+//
+//     this.showLabel();
+//   }
+//
+//   hideLineDash() {
+//
+//     this.lineDashFlag = false;
+//
+//     if (this.arrow) {
+//       this.arrow.visible = false;
+//     }
+//
+//     this.hideLabel();
+//   }
+//
+//
+//
+//   drawArrow() {
+//     if (!this.arrow) { return; }
+//
+//     const rect = this.getBoundingBox();
+//     this.arrow.x = rect.x + rect.width;
+//     this.arrow.y = rect.y;
+//
+//     this.arrow.update();
+//   }
+//
+//   drawFrame() {
+//
+//
+//     this.ctx.save();
+//
+//
+//     const rect = this.getBoundingBox();
+//
+//     const w = rect.width;
+//     const h = rect.height;
+//     const x = rect.x + w / 2;
+//     const y = rect.y + h / 2;
+//
+//     this.ctx.setLineDash([5, 5]);
+//     this.ctx.lineWidth = 2;
+//     this.ctx.strokeStyle = '#1bfff7';
+//     // this.ctx.fillStyle = '#ffffff';
+//
+//     this.ctx.beginPath();
+//
+//     this.ctx.moveTo( x - w / 2, y - h / 2);
+//
+//     this.ctx.lineTo(x + w / 2, y - h / 2);
+//
+//     this.ctx.lineTo(x + w / 2, y + h / 2);
+//
+//     this.ctx.lineTo(x - w / 2, y + h / 2);
+//
+//     this.ctx.lineTo(x - w / 2, y - h / 2);
+//
+//     // this.ctx.fill();
+//     this.ctx.stroke();
+//
+//
+//
+//
+//     this.ctx.restore();
+//
+//   }
+//
+//   draw() {
+//     super.draw();
+//
+//     if (this.lineDashFlag) {
+//       this.drawFrame();
+//       this.drawArrow();
+//     }
+//   }
+// }
+//
+//
+//
+// export class Label extends MySprite {
+//
+//   text:String;
+//   fontSize:String = '40px';
+//   fontName:String = 'Verdana';
+//   textAlign:String = 'left';
+//
+//
+//   constructor(ctx) {
+//     super(ctx);
+//     this.init();
+//   }
+//
+//   drawText() {
+//
+//     // console.log('in drawText', this.text);
+//
+//     if (!this.text) { return; }
+//
+//     this.ctx.font = `${this.fontSize} ${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);
+//
+//
+//   }
+//
+//
+//   drawSelf() {
+//     super.drawSelf();
+//     this.drawText();
+//   }
+//
+// }
+//
+//
+//
+// 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 = p2x - p1x;
+//   // const _y = p2y - p1y;
+//   // const tan = _y / _x;
+//   //
+//   // const radina = Math.atan(tan); // 用反三角函数求弧度
+//   // const angle = Math.floor(180 / (Math.PI / radina)); //
+//   //
+//   // console.log('r: ' , angle);
+//   // return angle;
+//   //
+//
+//
+//
+//   const x = Math.abs(px - mx);
+//   const y = Math.abs(py - my);
+//   // const x = Math.abs(mx - px);
+//   // const y = Math.abs(my - py);
+//   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;
+//
+// }
diff --git a/src/app/common/custom-hot-zone/custom-hot-zone.component.html b/src/app/common/custom-hot-zone/custom-hot-zone.component.html
index 273e0d5..9028527 100644
--- a/src/app/common/custom-hot-zone/custom-hot-zone.component.html
+++ b/src/app/common/custom-hot-zone/custom-hot-zone.component.html
@@ -1,14 +1,11 @@
 <div class="p-image-children-editor">
 
-
   <h5 style="margin-left: 2.5%;"> preview: </h5>
 
   <div class="preview-box" #wrap>
     <canvas id="canvas" #canvas></canvas>
   </div>
 
-
-
   <div nz-row nzType="flex" nzAlign="middle">
     <div nz-col nzSpan="5" nzOffset="1">
 
@@ -20,41 +17,53 @@
           (imageUploaded)="onBackgroundUploadSuccess($event)">
         </app-upload-image-with-preview>
       </div>
-
     </div>
 
+    <div nz-col nzSpan="5" nzOffset="1" class="img-box"
+         *ngFor="let it of hotZoneArr; let i = index">
 
+      <div
+        style="margin: auto; padding: 5px;  margin-top: 30px; width:90%; border: 2px dashed #ddd; border-radius: 10px">
+        <span style="margin-left: 40%;"> item-{{i + 1}}
+        </span>
+        <button style="float: right;" nz-button nzType="danger" nzSize="small" (click)="deleteBtnClick(i)">
+          X
+        </button>
 
-    <div  nz-col nzSpan="5" nzOffset="1" class="img-box"
-      *ngFor="let it of hotZoneArr; let i = index" >
-
-      <div style=" height: 40px;">
-        <h5> item-{{i+1}}
+        <nz-divider style="margin-top: 10px;"></nz-divider>
 
-          <i style="margin-left: 20px; margin-top: 2px; float: right; cursor:pointer" (click)="deleteItem($event, i)"
-             nz-icon [nzTheme]="'twotone'"  [nzType]="'close-circle'" [nzTwotoneColor]="'#ff0000'"></i>
-        </h5>
-      </div>
 
+        <div style="margin-top: -20px; margin-bottom: 5px">
+          <nz-radio-group [ngModel]="it.itemType" (ngModelChange)="radioChange($event, it)">
+            <label *ngIf="isHasRect" nz-radio nzValue="rect">矩形</label>
+            <label *ngIf="isHasPic" nz-radio nzValue="pic">图片</label>
+            <label *ngIf="isHasText" nz-radio nzValue="text">文本</label>
+          </nz-radio-group>
+        </div>
 
-      <!--<div class="img-box-upload">-->
-        <!--<app-upload-image-with-preview-->
-          <!--[picUrl]="it.pic_url"-->
-          <!--(imageUploaded)="onImgUploadSuccessByImg($event, it)">-->
-        <!--</app-upload-image-with-preview>-->
-      <!--</div>-->
+        <div *ngIf="it.itemType == 'pic'">
+          <app-upload-image-with-preview
+            [picUrl]="it?.pic_url"
+            (imageUploaded)="onItemImgUploadSuccess($event, it)">
+          </app-upload-image-with-preview>
+        </div>
 
+        <div *ngIf="it.itemType == 'text'">
+          <input type="text" nz-input [(ngModel)]="it.text" (blur)="saveText(it)">
+        </div>
 
-      <!--<app-audio-recorder-->
-        <!--[audioUrl]="it.audio_url ? it.audio_url : null "-->
-        <!--(audioUploaded)="onAudioUploadSuccessByImg($event, it)"-->
-      <!--&gt;</app-audio-recorder>-->
+        <div style="width: 100%; margin-top: 5px;">
+          <app-audio-recorder
+            [audioUrl]="it.audio_url"
+            (audioUploaded)="onItemAudioUploadSuccess($event, it)"
+          ></app-audio-recorder>
+        </div>
 
+      </div>
 
     </div>
 
 
-
     <div nz-col nzSpan="5" nzOffset="1">
 
       <div class="bg-box">
@@ -75,7 +84,7 @@
   <div class="save-box">
 
     <button class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
-      (click)="saveClick()">
+            (click)="saveClick()" >
       <i nz-icon nzType="save"></i>
       Save
     </button>
@@ -83,7 +92,9 @@
   </div>
 
 
-
 </div>
 
 
+<label style="opacity: 0; position: absolute; top: 0px; font-family: 'BRLNSR_1'">1</label>
+
+
diff --git a/src/app/common/custom-hot-zone/custom-hot-zone.component.scss b/src/app/common/custom-hot-zone/custom-hot-zone.component.scss
index dbfd8a6..9e2c2bf 100644
--- a/src/app/common/custom-hot-zone/custom-hot-zone.component.scss
+++ b/src/app/common/custom-hot-zone/custom-hot-zone.component.scss
@@ -87,6 +87,13 @@ h5 {
 
 
 
+@font-face
+{
+  font-family: 'BRLNSR_1';
+  src: url("/assets/font/BRLNSR_1.TTF") ;
+}
+
+
 
 
 
diff --git a/src/app/common/custom-hot-zone/custom-hot-zone.component.ts b/src/app/common/custom-hot-zone/custom-hot-zone.component.ts
index 6eed242..dc2a4cf 100644
--- a/src/app/common/custom-hot-zone/custom-hot-zone.component.ts
+++ b/src/app/common/custom-hot-zone/custom-hot-zone.component.ts
@@ -1,8 +1,21 @@
-import {Component, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, ViewChild} from '@angular/core';
+import {
+  Component,
+  ElementRef,
+  EventEmitter,
+  HostListener,
+  Input,
+  OnChanges,
+  OnDestroy,
+  OnInit,
+  Output,
+  ViewChild
+} from '@angular/core';
 import {Subject} from 'rxjs';
 import {debounceTime} from 'rxjs/operators';
-import {EditorItem, HotZoneItem, Label, MySprite} from './Unit';
+import {EditorItem, HotZoneImg, HotZoneItem, HotZoneLabel, Label, MySprite, removeItemFromArr} from './Unit';
 import TWEEN from '@tweenjs/tween.js';
+import {getMinScale} from "../../play/Unit";
+import {tar} from "compressing";
 
 
 @Component({
@@ -13,89 +26,85 @@ import TWEEN from '@tweenjs/tween.js';
 export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
 
-
-
-  _bgItem = null;
-
-  @Input()
-  set bgItem(v) {
-    this._bgItem = v;
-
-    this.init();
-  }
-  get bgItem() {
-    return this._bgItem;
-  }
-
   @Input()
   imgItemArr = null;
-
   @Input()
   hotZoneItemArr = null;
-
-
-
   @Input()
   hotZoneArr = null;
-
-
   @Output()
   save = new EventEmitter();
+  @ViewChild('canvas', {static: true}) canvas: ElementRef;
+  @ViewChild('wrap', {static: true}) wrap: ElementRef;
 
-  @ViewChild('canvas', {static: true }) canvas: ElementRef;
-  @ViewChild('wrap', {static: true }) wrap: ElementRef;
-  // @HostListener('window:resize', ['$event'])
+  @Input()
+  isHasRect = true;
+  @Input()
+  isHasPic = true;
+  @Input()
+  isHasText = true;
+  @Input()
+  hotZoneFontObj = {
+    size: 50,
+    name: 'BRLNSR_1',
+    color: '#8f3758'
+  }
+  @Input()
+  defaultItemType = 'text';
+
+  @Input()
+  hotZoneImgSize = 190;
+
+  saveDisabled = true;
 
   canvasWidth = 1280;
   canvasHeight = 720;
-
   canvasBaseW = 1280;
+  // @HostListener('window:resize', ['$event'])
   canvasBaseH = 720;
   mapScale = 1;
-
   ctx;
   mx;
   my; // 点击坐标
-
-
-  // 资源
-  // rawImages = new Map(res);
-
   // 声音
   bgAudio = new Audio();
-
-
   images = new Map();
-
   animationId: any;
-  // winResizeEventStream = new Subject();
 
+
+  // 资源
+  // rawImages = new Map(res);
+  winResizeEventStream = new Subject();
   canvasLeft;
   canvasTop;
-
   renderArr;
-
   imgArr = [];
-
   oldPos;
-
-
-
+  radioValue;
   curItem;
   bg: MySprite;
   changeSizeFlag = false;
   changeTopSizeFlag = false;
   changeRightSizeFlag = false;
 
+  constructor() {
+  }
 
+  _bgItem = null;
 
-  constructor() {
+  get bgItem() {
+    return this._bgItem;
   }
 
+  @Input()
+  set bgItem(v) {
+    this._bgItem = v;
 
+    this.init();
+  }
 
   onResize(event) {
-    // this.winResizeEventStream.next();
+    this.winResizeEventStream.next();
   }
 
 
@@ -118,13 +127,22 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   }
 
 
-
   onBackgroundUploadSuccess(e) {
     console.log('e: ', e);
     this.bgItem.url = e.url;
     this.refreshBackground();
   }
 
+  onItemImgUploadSuccess(e, item) {
+    item.pic_url = e.url;
+    this.loadHotZonePic(item.pic, e.url);
+  }
+
+  onItemAudioUploadSuccess(e, item) {
+    item.audio_url = e.url;
+  }
+
+
   refreshBackground(callBack = null) {
 
     if (!this.bg) {
@@ -162,14 +180,25 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     const item = this.getHotZoneItem();
     this.hotZoneArr.push(item);
 
+    this.refreshItem(item);
+
+    this.refreshHotZoneId();
+
+  }
+
+  deleteBtnClick(index) {
+
+    const item = this.hotZoneArr.splice(index, 1)[0];
+    removeItemFromArr(this.renderArr, item.pic);
+    removeItemFromArr(this.renderArr, item.textLabel);
+
     this.refreshHotZoneId();
 
-    console.log('hotZoneArr:', this.hotZoneArr);
   }
 
   onImgUploadSuccessByImg(e, img) {
     img.pic_url = e.url;
-    this.refreshImage (img);
+    this.refreshImage(img);
   }
 
   refreshImage(img) {
@@ -188,7 +217,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
       this.hotZoneArr[i].index = i;
 
       if (this.hotZoneArr[i]) {
-        this.hotZoneArr[i].text = 'item-' + (i + 1);
+        this.hotZoneArr[i].title = 'item-' + (i + 1);
 
       }
     }
@@ -206,7 +235,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
   }
 
-  getHotZoneItem( saveData = null) {
+  getHotZoneItem(saveData = null) {
 
 
     const itemW = 200;
@@ -219,18 +248,46 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     item.x = this.canvasWidth / 2;
     item.y = this.canvasHeight / 2;
 
+    item.itemType = this.defaultItemType;
+
     if (saveData) {
 
       const saveRect = saveData.rect;
       item.scaleX = saveRect.width / item.width;
       item.scaleY = saveRect.height / item.height;
-      item.x = saveRect.x + saveRect.width / 2 ;
+      item.x = saveRect.x + saveRect.width / 2;
       item.y = saveRect.y + saveRect.height / 2;
 
     }
 
     item.showLineDash();
 
+    const pic = new HotZoneImg(this.ctx);
+    pic.visible = false;
+    item['pic'] = pic;
+    if (saveData && saveData.pic_url) {
+      this.loadHotZonePic(pic, saveData.pic_url);
+    }
+    pic.x = item.x;
+    pic.y = item.y;
+    this.renderArr.push(pic);
+
+    const textLabel = new HotZoneLabel(this.ctx);
+    textLabel.fontSize = this.hotZoneFontObj.size;
+    textLabel.fontName = this.hotZoneFontObj.name;
+    textLabel.fontColor = this.hotZoneFontObj.color;
+    textLabel.textAlign = 'center';
+    // textLabel.setOutline();
+    // console.log('saveData:', saveData);
+    item['textLabel'] = textLabel;
+    textLabel.setScaleXY(this.mapScale);
+    if (saveData && saveData.text) {
+      textLabel.text = saveData.text;
+      textLabel.refreshSize();
+    }
+    textLabel.x = item.x;
+    textLabel.y = item.y;
+    this.renderArr.push(textLabel);
 
     return item;
   }
@@ -240,7 +297,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
 
     const item = new EditorItem(this.ctx);
-    item.load(img.pic_url).then( img => {
+    item.load(img.pic_url).then(img => {
 
       let maxW, maxH;
       if (this.bg) {
@@ -266,7 +323,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
         const saveRect = saveData.rect;
         item.setScaleXY(saveRect.width / item.width);
-        item.x = saveRect.x + saveRect.width / 2 ;
+        item.x = saveRect.x + saveRect.width / 2;
         item.y = saveRect.y + saveRect.height / 2;
 
       } else {
@@ -291,9 +348,29 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   }
 
 
+  radioChange(e, item) {
+    item.itemType = e;
+
+    this.refreshItem(item);
 
+    // console.log(' in radioChange e: ', e);
+  }
 
+  refreshItem(item) {
+    switch (item.itemType) {
+      case 'rect':
+        this.setRectState(item);
+        break;
+      case 'pic':
+        this.setPicState(item);
+        break;
+      case 'text':
+        this.setTextState(item);
+        break;
+      default:
 
+    }
+  }
 
 
   init() {
@@ -367,20 +444,25 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
       // img['audio_url'] = arr[i].audio_url;
       // this.imgArr.push(img);
 
-      const item = this.getHotZoneItem( data);
+      const item = this.getHotZoneItem(data);
+      item.audio_url = data.audio_url;
+      item.pic_url = data.pic_url;
+      item.text = data.text;
+      item.itemType = data.itemType;
+      this.refreshItem(item);
+
       console.log('item: ', item);
       this.hotZoneArr.push(item);
 
     }
 
     this.refreshHotZoneId();
+
     // this.refreshImageId();
 
   }
 
 
-
-
   initImgArr() {
 
     console.log('this.imgItemArr: ', this.imgItemArr);
@@ -446,34 +528,40 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
     this.oldPos = {x: this.mx, y: this.my};
 
-    const arr = this.hotZoneArr;
-
-    for (let i = arr.length - 1; i >= 0 ; i--) {
-      const item = arr[i];
+    for (let i = 0; i < this.hotZoneArr.length; i++) {
 
-      if (item) {
-        if (this.checkClickTarget(item)) {
+      const item = this.hotZoneArr[i];
+      let callback;
+      let target;
+      switch (item.itemType) {
+        case 'rect':
+          target = item;
+          callback = this.clickedHotZoneRect.bind(this);
+          break;
+        case 'pic':
+          target = item.pic;
+          callback = this.clickedHotZonePic.bind(this);
+          break;
+        case 'text':
+          target = item.textLabel;
+          callback = this.clickedHotZoneText.bind(this);
+          break;
+      }
 
-          if (item.lineDashFlag && this.checkClickTarget(item.arrow)) {
-            this.changeItemSize(item);
-          } else if (item.lineDashFlag && this.checkClickTarget(item.arrowTop)) {
-            this.changeItemTopSize(item);
-          } else if (item.lineDashFlag && this.checkClickTarget(item.arrowRight)) {
-            this.changeItemRightSize(item);
-          } else {
-            this.changeCurItem(item);
-          }
-          return;
-        }
+      if (this.checkClickTarget(target)) {
+        callback(target);
+        return;
       }
     }
 
 
-    // this.hideAllLineDash();
   }
 
+
   mapMove(event) {
-    if (!this.curItem) { return; }
+    if (!this.curItem) {
+      return;
+    }
 
     if (this.changeSizeFlag) {
       this.changeSize();
@@ -493,6 +581,9 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     }
 
     this.oldPos = {x: this.mx, y: this.my};
+
+    this.saveDisabled = true;
+
   }
 
   mapUp(event) {
@@ -503,13 +594,11 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   }
 
 
-
-
   changeSize() {
     const rect = this.curItem.getBoundingBox();
 
-    let lenW = ( this.mx - (rect.x + rect.width / 2) ) * 2;
-    let lenH = ( (rect.y + rect.height / 2) - this.my ) * 2;
+    let lenW = (this.mx - (rect.x + rect.width / 2)) * 2;
+    let lenH = ((rect.y + rect.height / 2) - this.my) * 2;
 
 
     let minLen = 20;
@@ -538,7 +627,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     const rect = this.curItem.getBoundingBox();
 
     // let lenW = ( this.mx - (rect.x + rect.width / 2) ) * 2;
-    let lenH = ( (rect.y + rect.height / 2) - this.my ) * 2;
+    let lenH = ((rect.y + rect.height / 2) - this.my) * 2;
 
 
     let minLen = 20;
@@ -550,10 +639,10 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     //   s = lenW / this.curItem.width;
     //
     // } else {
-      if (lenH < minLen) {
-        lenH = minLen;
-      }
-      s = lenH / this.curItem.height;
+    if (lenH < minLen) {
+      lenH = minLen;
+    }
+    s = lenH / this.curItem.height;
     // }
 
 
@@ -565,7 +654,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   changeRightSize() {
     const rect = this.curItem.getBoundingBox();
 
-    let lenW = ( this.mx - (rect.x + rect.width / 2) ) * 2;
+    let lenW = (this.mx - (rect.x + rect.width / 2)) * 2;
     // let lenH = ( (rect.y + rect.height / 2) - this.my ) * 2;
 
     let minLen = 20;
@@ -634,6 +723,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     // }
 
     this.updateArr(this.hotZoneArr);
+    this.updatePos()
 
 
     TWEEN.update();
@@ -648,7 +738,6 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   }
 
 
-
   renderAfterResize() {
     this.canvasWidth = this.wrap.nativeElement.clientWidth;
     this.canvasHeight = this.wrap.nativeElement.clientHeight;
@@ -657,11 +746,11 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
   initListener() {
 
-    // this.winResizeEventStream
-    //   .pipe(debounceTime(500))
-    //   .subscribe(data => {
-    //     this.renderAfterResize();
-    //   });
+    this.winResizeEventStream
+      .pipe(debounceTime(500))
+      .subscribe(data => {
+        this.renderAfterResize();
+      });
 
     if (this.IsPC()) {
 
@@ -767,41 +856,38 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     if (this.bg) {
       bgItem['rect'] = this.bg.getBoundingBox();
     } else {
-      bgItem['rect'] = {x: 0, y: 0, width: Math.round(this.canvasWidth * 100) / 100, height: Math.round(this.canvasHeight * 100) / 100};
+      bgItem['rect'] = {
+        x: 0,
+        y: 0,
+        width: Math.round(this.canvasWidth * 100) / 100,
+        height: Math.round(this.canvasHeight * 100) / 100
+      };
     }
 
 
-
-    // const imgItemArr = [];
-    // const imgArr = this.imgArr;
-    // for (let i = 0; i < imgArr.length; i++) {
-    //
-    //   const imgItem = {
-    //     id: imgArr[i].id,
-    //     pic_url: imgArr[i].pic_url,
-    //     audio_url: imgArr[i].audio_url,
-    //   };
-    //   if (imgArr[i].picItem) {
-    //     imgItem['rect'] = imgArr[i].picItem.getBoundingBox();
-    //     imgItem['rect'].x -= bgItem['rect'].x;
-    //     imgItem['rect'].y -= bgItem['rect'].y;
-    //   }
-    //   imgItemArr.push(imgItem);
-    // }
-
-
     const hotZoneItemArr = [];
     const hotZoneArr = this.hotZoneArr;
     for (let i = 0; i < hotZoneArr.length; i++) {
 
       const hotZoneItem = {
         index: hotZoneArr[i].index,
+        pic_url: hotZoneArr[i].pic_url,
+        text: hotZoneArr[i].text,
+        audio_url: hotZoneArr[i].audio_url,
+        itemType: hotZoneArr[i].itemType,
+        fontSize: this.hotZoneFontObj.size,
+        fontName: this.hotZoneFontObj.name,
+        fontColor: this.hotZoneFontObj.color,
+        fontScale: hotZoneArr[i].textLabel ? hotZoneArr[i].textLabel.scaleX : 1,
+        imgScale: hotZoneArr[i].pic ? hotZoneArr[i].pic.scaleX : 1,
+        mapScale: this.mapScale
       };
       hotZoneItem['rect'] = hotZoneArr[i].getBoundingBox();
-      hotZoneItem['rect'].x = Math.round( (hotZoneItem['rect'].x - bgItem['rect'].x) * 100) / 100;
-      hotZoneItem['rect'].y = Math.round( (hotZoneItem['rect'].y - bgItem['rect'].y) * 100) / 100;
-      hotZoneItem['rect'].width = Math.round( (hotZoneItem['rect'].width) * 100) / 100;
-      hotZoneItem['rect'].height = Math.round( (hotZoneItem['rect'].height) * 100) / 100;
+      hotZoneItem['rect'].x = Math.round((hotZoneItem['rect'].x - bgItem['rect'].x) * 100) / 100;
+      hotZoneItem['rect'].y = Math.round((hotZoneItem['rect'].y - bgItem['rect'].y) * 100) / 100;
+      hotZoneItem['rect'].width = Math.round((hotZoneItem['rect'].width) * 100) / 100;
+      hotZoneItem['rect'].height = Math.round((hotZoneItem['rect'].height) * 100) / 100;
+
 
       hotZoneItemArr.push(hotZoneItem);
     }
@@ -810,4 +896,91 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
     this.save.emit({bgItem, hotZoneItemArr});
   }
+
+  private updatePos() {
+    this.hotZoneArr.forEach((item) => {
+      let x, y;
+      switch (item.itemType) {
+        case 'rect':
+          x = item.x;
+          y = item.y;
+          break;
+
+        case 'pic':
+          x = item.pic.x;
+          y = item.pic.y;
+          break;
+
+        case 'text':
+          x = item.textLabel.x;
+          y = item.textLabel.y;
+          break;
+      }
+
+      item.x = x;
+      item.y = y;
+      item.pic.x = x;
+      item.pic.y = y;
+      item.textLabel.x = x;
+      item.textLabel.y = y;
+    });
+  }
+
+  private setPicState(item: any) {
+    item.visible = false;
+    item.textLabel.visible = false;
+    item.pic.visible = true;
+  }
+
+  private setRectState(item: any) {
+    item.visible = true;
+    item.textLabel.visible = false;
+    item.pic.visible = false;
+  }
+
+  private setTextState(item: any) {
+    item.visible = false;
+    item.pic.visible = false;
+    item.textLabel.visible = true;
+  }
+
+  private clickedHotZoneRect(item: any) {
+    if (this.checkClickTarget(item)) {
+
+      if (item.lineDashFlag && this.checkClickTarget(item.arrow)) {
+        this.changeItemSize(item);
+      } else if (item.lineDashFlag && this.checkClickTarget(item.arrowTop)) {
+        this.changeItemTopSize(item);
+      } else if (item.lineDashFlag && this.checkClickTarget(item.arrowRight)) {
+        this.changeItemRightSize(item);
+      } else {
+        this.changeCurItem(item);
+      }
+      return;
+    }
+  }
+
+  private clickedHotZonePic(item: any) {
+    if (this.checkClickTarget(item)) {
+      this.curItem = item;
+    }
+  }
+
+  private clickedHotZoneText(item: any) {
+    if (this.checkClickTarget(item)) {
+      this.curItem = item;
+    }
+  }
+
+  saveText(item) {
+    item.textLabel.text = item.text;
+  }
+
+  private loadHotZonePic(pic: HotZoneImg, url) {
+    const baseLen = this.hotZoneImgSize * this.mapScale;
+    pic.load(url).then(() => {
+      const s = getMinScale(pic, baseLen);
+      pic.setScaleXY(s);
+    });
+  }
 }
diff --git a/tsconfig.app.json b/tsconfig.app.json
index 565a11a..8fe350f 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -2,7 +2,9 @@
   "extends": "./tsconfig.json",
   "compilerOptions": {
     "outDir": "./out-tsc/app",
-    "types": []
+    "types": [
+      "node"
+    ]
   },
   "files": [
     "src/main.ts",
-- 
2.21.0