|  | @@ -0,0 +1,267 @@
 | 
	
		
			
				|  |  | +<template>
 | 
	
		
			
				|  |  | +  <canvas ref="canvas" id="panel1"></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: function (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.onmouseout = (e) => {
 | 
	
		
			
				|  |  | +        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.onmouseup = (e) => {
 | 
	
		
			
				|  |  | +        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++;
 | 
	
		
			
				|  |  | +          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(1 * this.sratio, 1 * 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;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        var 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();
 | 
	
		
			
				|  |  | +        var 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);
 | 
	
		
			
				|  |  | +          var 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) {
 | 
	
		
			
				|  |  | +      var topX = this.$refs.canvas.width;
 | 
	
		
			
				|  |  | +      var btmX = 0;
 | 
	
		
			
				|  |  | +      var topY = this.$refs.canvas.height;
 | 
	
		
			
				|  |  | +      var btnY = 0;
 | 
	
		
			
				|  |  | +      for (var i = 0; i < this.$refs.canvas.width; i++) {
 | 
	
		
			
				|  |  | +        for (var j = 0; j < this.$refs.canvas.height; j++) {
 | 
	
		
			
				|  |  | +          var 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++;
 | 
	
		
			
				|  |  | +      btmX++;
 | 
	
		
			
				|  |  | +      topY++;
 | 
	
		
			
				|  |  | +      btnY++;
 | 
	
		
			
				|  |  | +      const data = [topX, topY, btmX, btnY];
 | 
	
		
			
				|  |  | +      return data;
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +</script>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +<style scoped>
 | 
	
		
			
				|  |  | +canvas {
 | 
	
		
			
				|  |  | +  max-width: 100%;
 | 
	
		
			
				|  |  | +  display: block;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +</style>
 |