<template>
  <canvas id="panel1" ref="canvas"></canvas>
</template>

<script>
export default {
  props: {
    width: {
      type: Number,
      default: 800,
    },
    height: {
      type: Number,
      default: 300,
    },
    lineWidth: {
      type: Number,
      default: 4,
    },
    lineColor: {
      type: String,
      default: '#000000',
    },
    bgColor: {
      type: String,
      default: '',
    },
    isCrop: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      hasDrew: false,
      resultImg: '',
      canvasTxt: null,
      canvas: null,
      redrawCanvas: null,
      isMouseDown: false,
      lastLoc: { x: 0, y: 0 },
      history: [],
      sratio: 1,
    };
  },
  computed: {
    ratio() {
      return this.height / this.width;
    },
    stageInfo() {
      return this.$refs.canvas.getBoundingClientRect();
    },
    myBg() {
      return this.bgColor ? this.bgColor : 'rgba(255, 255, 255, 0)';
    },
    context() {
      return this.$refs.canvas.getContext('2d');
    },
    redrawCxt() {
      return this.$refs.canvas.getContext('2d');
    },
  },
  watch: {
    myBg(newVal) {
      this.$refs.canvas.style.background = newVal;
    },
  },
  beforeMount() {
    window.addEventListener('resize', this.$_resizeHandler);
  },
  beforeDestroy() {
    window.removeEventListener('resize', this.$_resizeHandler);
  },
  mounted() {
    const canvas = this.$refs.canvas;
    canvas.height = this.height;
    canvas.width = this.width;
    canvas.style.background = this.myBg;
    this.$_resizeHandler();
    // 在画板以外松开鼠标后冻结画笔
    document.onmouseup = () => {
      this.isMouseDown = false;
    };
    this.canvas = canvas;
    this.redrawCanvas = canvas;
    this.init();
  },
  methods: {
    reload() {
      this.reset();
      this.redraw(this.redrawCxt);
    },
    init() {
      this.canvas.onmousedown = (e) => {
        this.isMouseDown = true;
        this.hasDrew = true;
        this.lastLoc = this.window2Canvas(e.clientX, e.clientY);
      };
      this.canvas.ontouchstart = (e) => {
        this.isMouseDown = true;
        this.hasDrew = true;
        this.lastLoc = this.window2Canvas(e.touches[0].clientX, e.touches[0].clientY);
      };
      this.canvas.onmouseout = () => {
        this.isMouseDown = false;
      };
      this.canvas.onmousemove = (e) => {
        if (this.isMouseDown) {
          let curLoc = this.window2Canvas(e.clientX, e.clientY); // 获得当前坐标
          this.draw(this.context, this.lastLoc, curLoc);
          this.history.push([this.lastLoc, curLoc]);
          this.lastLoc = Object.assign({}, curLoc); // U know what I mean.
        }
      };
      this.canvas.ontouchmove = (e) => {
        if (this.isMouseDown) {
          let curLoc = this.window2Canvas(e.touches[0].clientX, e.touches[0].clientY); // 获得当前坐标
          this.draw(this.context, this.lastLoc, curLoc);
          this.history.push([this.lastLoc, curLoc]);
          this.lastLoc = Object.assign({}, curLoc); // U know what I mean.
        }
      };
      this.canvas.onmouseup = () => {
        this.isMouseDown = false;
        if (history.length) {
          localStorage.setItem('history', JSON.stringify(this.history));
        }
      };
      this.canvas.onTouchend = () => {
        this.isMouseDown = false;
        if (history.length) {
          localStorage.setItem('history', JSON.stringify(this.history));
        }
      };
    },
    window2Canvas(x, y) {
      let bbox = this.canvas.getBoundingClientRect();
      return { x: Math.round(x - bbox.left), y: Math.round(y - bbox.top) };
    },
    draw(context, lastLoc, curLoc) {
      if (context) {
        context.lineWidth = 5;
        context.beginPath();
        context.moveTo(lastLoc.x, lastLoc.y);
        context.lineTo(curLoc.x, curLoc.y);
        context.strokeStyle = '#000';
        context.lineCap = 'round';
        context.lineJoin = 'round';
        context.stroke();
      } else {
        this.redrawCxt.lineWidth = 5;
        this.redrawCxt.beginPath();
        this.redrawCxt.moveTo(lastLoc.x, lastLoc.y);
        this.redrawCxt.lineTo(curLoc.x, curLoc.y);
        this.redrawCxt.strokeStyle = '#000';
        this.redrawCxt.lineCap = 'round';
        this.redrawCxt.lineJoin = 'round';
        this.redrawCxt.stroke();
      }
    },
    redraw(context) {
      if (localStorage.getItem('history')) {
        let history = JSON.parse(localStorage.getItem('history'));
        const len = history.length;
        let i = 0;
        const runner = () => {
          i += 1;
          if (i < len) {
            this.draw(context, history[i][0], history[i][1]);
            requestAnimationFrame(runner);
          }
        };
        requestAnimationFrame(runner);
      }
    },

    $_resizeHandler() {
      const canvas = this.$refs.canvas;
      canvas.style.width = `${this.width}px`;
      const realw = parseFloat(window.getComputedStyle(canvas).width);
      canvas.style.height = `${this.ratio * realw}px`;
      this.canvasTxt = canvas.getContext('2d');
      this.canvasTxt.scale(Number(this.sratio), Number(this.sratio));
      this.sratio = realw / this.width;
      this.canvasTxt.scale(1 / this.sratio, 1 / this.sratio);
    },
    // 操作
    generate() {
      const pm = new Promise((resolve, reject) => {
        if (!this.hasDrew) {
          reject(`Warning: Not Signned!`);
          return;
        }
        let resImgData = this.canvasTxt.getImageData(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
        this.canvasTxt.globalCompositeOperation = 'destination-over';
        this.canvasTxt.fillStyle = this.myBg;
        this.canvasTxt.fillRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
        this.resultImg = this.$refs.canvas.toDataURL();
        let resultImg = this.resultImg;
        this.canvasTxt.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
        this.canvasTxt.putImageData(resImgData, 0, 0);
        this.canvasTxt.globalCompositeOperation = 'source-over';
        if (this.isCrop) {
          const crop_area = this.getCropArea(resImgData.data);
          let crop_canvas = document.createElement('canvas');
          const crop_ctx = crop_canvas.getContext('2d');
          crop_canvas.width = crop_area[2] - crop_area[0];
          crop_canvas.height = crop_area[3] - crop_area[1];
          const crop_imgData = this.canvasTxt.getImageData(...crop_area);
          crop_ctx.globalCompositeOperation = 'destination-over';
          crop_ctx.putImageData(crop_imgData, 0, 0);
          crop_ctx.fillStyle = this.myBg;
          crop_ctx.fillRect(0, 0, crop_canvas.width, crop_canvas.height);
          resultImg = crop_canvas.toDataURL();
          crop_canvas = null;
        }
        resolve(resultImg);
      });
      return pm;
    },
    reset() {
      this.canvasTxt.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
      this.$emit('update:bgColor', '');
      this.$refs.canvas.style.background = 'rgba(255, 255, 255, 0)';
      this.history = [];
      this.hasDrew = false;
      this.resultImg = '';
    },
    getCropArea(imgData) {
      let topX = this.$refs.canvas.width;
      let btmX = 0;
      let topY = this.$refs.canvas.height;
      let btnY = 0;
      for (let i = 0; i < this.$refs.canvas.width; i++) {
        for (let j = 0; j < this.$refs.canvas.height; j++) {
          let pos = (i + this.$refs.canvas.width * j) * 4;
          if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
            btnY = Math.max(j, btnY);
            btmX = Math.max(i, btmX);
            topY = Math.min(j, topY);
            topX = Math.min(i, topX);
          }
        }
      }
      topX += 1;
      btmX += 1;
      topY += 1;
      btnY += 1;
      const data = [topX, topY, btmX, btnY];
      return data;
    },
  },
};
</script>

<style scoped>
canvas {
  display: block;
  max-width: 100%;
}
</style>
