<template>
  <canvas class="drawCanvas" v-show="visible" ref="canvas" @contextmenu="canvasContextMenu" :width="canvasWidth"
    :height="canvasHeight">
  </canvas>
</template>

<script>

/**
 * 所有图形类都继承形状基类
 * 每个图形自己实现相关交互（包括不限于响应：点击，鼠标移动，鼠标按下，鼠标抬起等事件）
 * 与canvas拖拽鼠标按下相关事件区分开
 * 这样更好维护
 */
class Shape {
  //绘制方法
  draw () { }
  startDraw () { }
  stopDraw () { }
  getData () { }
  setData () { }
  constructor(canvasEl, context, hub) {
    this.type = ''
    this.canvasEl = canvasEl;
    this.context = context;
    // 鼠标事件缓存坐标
    this.x1 = 0;
    this.y1 = 0;
    //拖拽标记
    this.isDragging = false;
    //拉伸标记，默认false
    this.isStretching = false;
    //活动标记 只有出现活动标记时，才追加拉伸框
    this.isActive = false;
    //是否正在绘制
    this.isDrawing = false;
    //事件回调总线
    this.hub = hub;
  }

  stopDraw () {
    this.isDragging = false;
    this.canvasEl.onmouseup = null;
    this.canvasEl.onmousemove = null;
    this.canvasEl.onmousedown = null;
  }
}
/**
 * 多边形
 */
class Polygon extends Shape {
  constructor(hub, canvas, ctx) {
    super(canvas, ctx, hub);
    this.points = [];
    this.type = 'Polygon'
    this.clickCount = 0
  }
  setData (data) {
    const temp = data.split(',')
    const x = temp[0]
    const y = temp[1]
    const w = temp[2]
    const h = temp[3]
    const cw = temp[4]
    const ch = temp[5]
  }
  getData () {
    let data = []
    this.points.forEach(p => {
      data = data.concat(p)
    })
    data = data.concat([this.canvasEl.clientWidth, this.canvasEl.clientHeight])
    return {
      type: 'Polygon',
      data: data.join(',')
    }
  }
  /**
   * 绘制方法
   */
  draw (point) {
    const rect = this;
    // 绘制矩形
    this.context.strokeStyle = rect.color || "#FF0000";
    this.context.clearRect(0, 0, this.canvasEl.clientWidth, this.canvasEl.clientHeight);
    // 如果有传入点，则追追加绘制
    const points = point ? this.points.concat([point]) : this.points;
    this.context.beginPath();
    this.context.moveTo(points[0][0], points[0][1]);
    for (let i = 1; i < points.length; i++) {
      this.context.lineTo(points[i][0], points[i][1]);
    }
    this.context.closePath();
    this.context.stroke();
  }
  startDraw () {
    this.isDrawing = true;
    this.clickCount = 0;
    const me = this;
    this.canvasEl.onmouseup = (e) => {
      me.mouseUp(e);
    };
    this.canvasEl.onmousemove = (e) => {
      me.mouseMove(e);
    };
  }
  mouseUp (e) {
    // 取得鼠标画布位置
    const point = [e.offsetX, e.offsetY];
    if (this.isDrawing) {
      // this.clickCount += 1;
      if (e.button === 2) {
        this.isDrawing = false;
        this.hub({
          key: 'drawFinish',
          data: this
        })
        this.draw();
      } else {
        this.points.push(point)
      }
      this.draw();

    }
    //清除标记
    this.isStretching = false;
    this.isDragging = false;
  }
  mouseMove (e) {
    // 取得鼠标画布位置
    const point = [e.offsetX, e.offsetY];
    if (this.isDrawing) {
      this.draw(point);
    }
  }
}
/**
 * 折线，非闭合，用于现场管控
 */
class BrokenLine extends Shape {
  constructor(hub, canvas, ctx) {
    super(canvas, ctx, hub);
    this.points = [];
    this.type = 'BrokenLine'
    this.clickCount = 0
    // 要画的线数
    this.lineCount = 0;
  }
  setData (data) {
    const temp = data.split(',')
    const x = temp[0]
    const y = temp[1]
    const w = temp[2]
    const h = temp[3]
    const cw = temp[4]
    const ch = temp[5]
  }
  getData () {
    let data = []
    this.points.forEach(p => {
      data = data.concat(p)
    })
    data = data.concat([this.canvasEl.clientWidth, this.canvasEl.clientHeight])
    return {
      type: 'BrokenLine',
      data: data.join(',')
    }
  }
  /**
   * 绘制方法
   */
  draw (point) {
    const rect = this;
    // 绘制矩形
    this.context.strokeStyle = rect.color || "#FF0000";
    this.context.clearRect(0, 0, this.canvasEl.clientWidth, this.canvasEl.clientHeight);
    // 如果有传入点，则追追加绘制
    const points = point ? this.points.concat([point]) : this.points;
    this.context.beginPath();
    this.context.moveTo(points[0][0], points[0][1]);
    for (let i = 1; i < points.length; i++) {
      this.context.lineTo(points[i][0], points[i][1]);
    }
    // this.context.closePath();
    this.context.stroke();
  }
  startDraw () {
    this.isDrawing = true;
    this.clickCount = 0;
    const me = this;
    this.canvasEl.onmouseup = (e) => {
      me.mouseUp(e);
    };
    this.canvasEl.onmousemove = (e) => {
      me.mouseMove(e);
    };
  }
  // 根据线数判断是否结束绘制
  isNeedOverDraw () {
    if (this.lineCount <= 0) {
      return false
    }

    // 两个点为一条线，所以-1
    return (this.lineCount <= this.clickCount - 1)
  }
  mouseUp (e) {
    // 取得鼠标画布位置
    const point = [e.offsetX, e.offsetY];
    if (this.isDrawing) {
      this.clickCount += 1;
      if (e.button === 2) {
        this.isDrawing = false;
        this.hub({
          key: 'drawFinish',
          data: this
        })
        this.draw();
      } else {
        this.points.push(point)
      }

      // 是否需要结束
      if (this.isNeedOverDraw()) {
        this.isDrawing = false;
        this.hub({
          key: 'drawFinish',
          data: this
        })
      }
      this.draw();
    }
    //清除标记
    this.isStretching = false;
    this.isDragging = false;
  }
  mouseMove (e) {
    // 取得鼠标画布位置
    const point = [e.offsetX, e.offsetY];
    if (this.isDrawing) {
      this.draw(point);
    }
  }
}
/**
 * 矩形类
 */
class Rectangle extends Shape {
  /**
 * 矩形类
 * @param canvasEl canvas 元素对象
 * @param context object canvas 上下文
 * @param x int 默认 0
 * @param y int 默认 0
 * @param width 宽度 默认 100
 * @param height 高度 默认 100
 * @param lineColr string 绘制线的颜色
 */
  constructor(hub, canvasEl, context, x, y, width, height, lineColor = '#FF0000') {
    //调用父类构造函数
    super(canvasEl, context, hub);
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.color = lineColor;
    this.type = 'Rectangle';
    this.clickCount = 0;
  }
  setData (data) {
    const temp = data.split(',')
    const x = temp[0]
    const y = temp[1]
    const w = temp[2]
    const h = temp[3]
    const cw = temp[4]
    const ch = temp[5]
  }
  getData () {
    // 调整顺序，按照顺时针绘制
    let data = [this.x, this.y, this.x + this.width, this.y, this.x + this.width, this.y + this.height, this.x, this.y + this.height, this.canvasEl.clientWidth, this.canvasEl.clientHeight]
    if (this.width < 0 && this.height > 0) {
      data = [this.x + this.width, this.y, this.x, this.y, this.x, this.y + this.height, this.x + this.width, this.y + this.height, this.canvasEl.clientWidth, this.canvasEl.clientHeight]
    }
    else if (this.width > 0 && this.height < 0) {
      data = [this.x, this.y + this.height, this.x + this.width, this.y + this.height, this.x + this.width, this.y, this.x, this.y, this.canvasEl.clientWidth, this.canvasEl.clientHeight]
    }
    else if (this.width < 0 && this.height < 0) {
      data = [this.x + this.width, this.y + this.height, this.x, this.y + this.height, this.x, this.y, this.x + this.width, this.y, this.canvasEl.clientWidth, this.canvasEl.clientHeight]
    }
    return {
      type: 'Rectangle',
      data: data.join(',')
    }
  }
  /**
   * 绘制方法
   */
  draw () {
    const rect = this;
    // 绘制矩形
    this.context.strokeStyle = rect.color || "#FF0000";
    if (this.isDrawing) {
      this.context.clearRect(0, 0, this.canvasEl.clientWidth, this.canvasEl.clientHeight);
    }
    // 绘制矩形到画布
    this.context.strokeRect(rect.x, rect.y, rect.width, rect.height);
  }
  startDraw () {
    this.isDrawing = true;
    this.clickCount = 0;
    const me = this;
    this.canvasEl.onmouseup = (e) => {
      me.mouseUp(e);
    };
    this.canvasEl.onmousemove = (e) => {
      me.mouseMove(e);
    };
  }
  mouseUp (e) {
    if (this.isDrawing) {
      this.clickCount += 1;
      if (this.clickCount === 1) {
        const point = [e.offsetX, e.offsetY]
        this.x = point[0];
        this.y = point[1];
      }
      if (this.clickCount > 1) {
        this.isDrawing = true;
        this.clickCount = 0
        this.hub({
          key: 'drawFinish',
          data: this
        })
      }
    }
    //清除标记
    this.isStretching = false;
    this.isDragging = false;
  }
  mouseMove (e) {

    // 取得鼠标画布位置
    const point = [e.offsetX, e.offsetY];

    if (this.isDrawing && this.clickCount > 0) {
      // 绘制
      this.width = point[0] - this.x;
      this.height = point[1] - this.y;
      this.draw();
    }
  }
}

/**
 * 点
 */
class Point extends Shape {
  constructor(hub, canvas, ctx) {
    super(canvas, ctx, hub);
    this.points = [];
    this.type = "Point";
    this.clickCount = 0;
  }
  setData (data) {
    const temp = data.split(",");
    const x = temp[0];
    const y = temp[1];
    const w = temp[2];
    const h = temp[3];
    const cw = temp[4];
    const ch = temp[5];
  }
  getData () {
    let data = [];
    this.points.forEach((p) => {
      data = data.concat(p);
    });
    data = data.concat([this.canvasEl.clientWidth, this.canvasEl.clientHeight]);
    return {
      type: "Point",
      data: data.join(","),
    };
  }
  /**
   * 绘制方法
   */
  draw (point) {
    const rect = this;
    // 绘制矩形
    this.context.strokeStyle = rect.color || "#FF0000";
    this.context.clearRect(
      0,
      0,
      this.canvasEl.clientWidth,
      this.canvasEl.clientHeight
    );
    // 如果有传入点，则追追加绘制
    const points = point ? this.points.concat([point]) : this.points;
    this.context.beginPath();
    this.context.arc(point[0], point[1], 5, 0, 360, false);
    // this.context.fillstyle="#FF0000";
    this.context.fill();
    // this.context.moveTo(points[0][0], points[0][1]);
    // for (let i = 1; i < points.length; i++) {
    //   this.context.lineTo(points[i][0], points[i][1]);
    // }
    // this.context.closePath();
    this.context.stroke();
  }
  startDraw () {
    this.isDrawing = true;
    this.clickCount = 0;
    const me = this;
    this.canvasEl.onmousedown = (e) => {
      me.mousedown(e);
    };
  }
  mousedown (e) {
    // if (!this.isDrawing) {
    //   return
    // }
    // 取得鼠标画布位置
    const point = [e.offsetX, e.offsetY];
    this.draw(point)
    this.points = [point]
    this.isDrawing = false;

    this.hub({
      key: 'drawFinish',
      data: this
    })
  }
}


const ShapeEnum = {
  "rect": "Rectangle",
  "brokenline": "BrokenLine",
  "polygon": "Polygon",
  "point": "Point",
}

class ShapeFactory {
  static createShape (shapeEnum, canvas, ctx, messageHub) {
    switch (shapeEnum) {
      case "Rectangle":
        return new Rectangle(messageHub, canvas, ctx, 0, 0, 10, 10);
      case "BrokenLine":
        return new BrokenLine(messageHub, canvas, ctx);
      case "Polygon":
        return new Polygon(messageHub, canvas, ctx);
      case "Point":
        return new Point(messageHub, canvas, ctx);
    }
  }
}


/**
  * 转换坐标系
  */
function toTargetRect (sourceRect, targetRect) {
  const sourceX = sourceRect[0], sourceY = sourceRect[1], sourceWidth = sourceRect[2], sourceHeight = sourceRect[3], sourceCanvasWidth = sourceRect[4], sourceCanvasHeight = sourceRect[5];
  let targetX = targetRect[0], targetY = targetRect[1], targetWidth = targetRect[2], targetHeight = targetRect[3], targetCanvasWidth = targetRect[4], targetCanvasHeight = targetRect[5];
  const scaleX = targetCanvasWidth / sourceCanvasWidth, scaleY = targetCanvasHeight / sourceCanvasHeight;
  targetX = sourceX * scaleX;
  targetY = sourceY * scaleY;
  targetWidth = sourceWidth * scaleX;
  targetHeight = sourceHeight * scaleY;
  return [targetX, targetY, targetWidth, targetHeight, targetCanvasWidth, targetCanvasHeight];
}

export {
  Rectangle,
  toTargetRect,
  ShapeEnum
}
import lodash from 'lodash'
export default {
  props: {
    // 折线的线数
    brokenLineCount: {
      type: Number,
      default: 0
    }
  },
  data () {
    return {
      visible: false,
      canvasWidth: 400,
      canvasHeight: 300,
      // canvas 元素
      canvas: null,
      context: null,
      //正在绘制的图形对象
      drawingShape: null
    }
  },
  methods: {
    canvasContextMenu (e) {
      e.cancelBubble = true
      e.preventDefault();
    },
    show () {
      this.visible = true
    },
    hide () {
      this.visible = false
    },
    startDraw (shapeEnum) {
      const me = this
      // 清除画布，准备绘制
      this.context.clearRect(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);
      if (this.drawingShape) {
        this.drawingShape.stopDraw()
      }
      this.drawingShape = ShapeFactory.createShape(shapeEnum, this.canvas, this.context, ({ key }) => {
        if (key === 'drawFinish') {
          me.stopDraw();
        }
      });
      // 折线可设置线数
      if (shapeEnum == ShapeEnum.brokenline) {
        this.drawingShape.lineCount = this.brokenLineCount
      }
      this.drawingShape.startDraw();
    },
    stopDraw () {
      this.$emit('drawComplete', lodash.cloneDeep(this.drawingShape));
    },
    clearShapes () {
      this.context.clearRect(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);
    },

    /**
     * 初始化事件
     */
    init () {
      this.canvasWidth = this.$refs.canvas.clientWidth;
      this.canvasHeight = this.$refs.canvas.clientHeight;
      this.canvas = this.$refs.canvas;
      this.context = this.canvas.getContext("2d");
    }
  }
}
</script>

<style scoped lang="scss">
.drawCanvas {
  box-sizing: border-box;
  position: absolute;
  left: 0;
  top: 0;
  border: 1px solid blueviolet;
  // background-color: rgba(230, 214, 245, 0.562);
  width: 100%;
  height: 100%;
  z-index: 100;
}
</style>

