<template>
  <div class="polygon-selector">
    <!--用来和鼠标进行交互操作的canvas-->
    <canvas 
      id="canvas" 
      ref="canvasRef" 
      :width="width + 'px'" 
      :height="height + 'px'"
      :style="{ backgroundImage: 'url(' + backgroundImg + ')', cursor: 'crosshair' }"
    ></canvas>
    <!--存储已生成的点线，避免被清空-->
    <canvas 
      id="canvasSave" 
      ref="canvasSaveRef"
      :width="width + 'px'" 
      :height="height + 'px'"
      class="canvas-save"
    ></canvas>
    <div class="controls">
      <el-button @click="clearSelection" type="primary">清空选区</el-button>
      <slot name="controls"></slot>
    </div>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue'

export default {
  name: 'PolygonSelector',
  props: {
    width: {
      type: [Number, String],
      default: 1222
    },
    height: {
      type: [Number, String],
      default: 789
    },
    backgroundImg: {
      type: String,
      default: '/images/img.png'
    }
  },
  emits: ['selection-complete', 'points-change'],
  setup(props, { emit }) {
    const canvasRef = ref(null)
    const canvasSaveRef = ref(null)
    
    let ctx = null
    let ctxSave = null
    let pointArr = [] // 存放坐标的数组
    let oIndex = -1 // 判断鼠标是否移动到起始点处，-1为否，1为是

    onMounted(() => {
      initCanvas()
    })

    const initCanvas = () => {
      const canvas = canvasRef.value
      const canvasSave = canvasSaveRef.value
      
      if (!canvas || !canvasSave) return
      
      ctx = canvas.getContext('2d')
      ctxSave = canvasSave.getContext('2d')
      
      // 设置样式
      ctx.strokeStyle = 'rgba(102,168,255,1)' // 线条颜色
      ctx.lineWidth = 4 // 线条粗细
      ctxSave.strokeStyle = 'rgba(102,168,255,1)' // 线条颜色
      ctxSave.lineWidth = 4 // 线条粗细
      
      // 绑定事件
      canvas.addEventListener('click', handleClick)
      canvas.addEventListener('mousemove', handleMouseMove)
    }
    
    const handleClick = (e) => {
      const canvas = canvasRef.value
      const rect = canvas.getBoundingClientRect()
      const pointX = e.clientX - rect.left
      const pointY = e.clientY - rect.top
      let piX, piY
      
      if (oIndex > 0 && pointArr.length > 0) {
        piX = pointArr[0].x
        piY = pointArr[0].y
        // 画点
        makearc(ctx, piX, piY, getRandomNum(2, 2), 0, 180, 'rgba(102,168,255,0.5)')
        pointArr.push({x: piX, y: piY})
        canvasSave(pointArr) // 保存点线同步到另一个canvas
        saveCanvas() // 生成画布
      } else {
        piX = pointX
        piY = pointY
        makearc(ctx, piX, piY, getRandomNum(2, 2), 0, 180, 'rgba(102,168,255,0.5)')
        pointArr.push({x: piX, y: piY})
        canvasSave(pointArr) // 保存点线同步到另一个canvas
      }
    }
    
    const handleMouseMove = (e) => {
      const canvas = canvasRef.value
      const rect = canvas.getBoundingClientRect()
      const pointX = e.clientX - rect.left
      const pointY = e.clientY - rect.top
      let piX, piY
      
      // 清空画布
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      // 鼠标下跟随的圆点
      makearc(ctx, pointX, pointY, getRandomNum(4, 4), 0, 180, 'rgba(102,168,255,1)')
      
      if (pointArr.length > 0) {
        if ((pointX > pointArr[0].x-15 && pointX < pointArr[0].x+15) && 
            (pointY > pointArr[0].y-15 && pointY < pointArr[0].y+15)) {
          if (pointArr.length > 1) {
            piX = pointArr[0].x
            piY = pointArr[0].y
            ctx.clearRect(0, 0, canvas.width, canvas.height)
            makearc(ctx, piX, piY, getRandomNum(4, 4), 0, 180, 'rgba(102,168,255,0.5)')
            oIndex = 1
          }
        } else {
          piX = pointX
          piY = pointY
          oIndex = -1
        }
        
        // 开始绘制
        ctx.beginPath()
        ctx.moveTo(pointArr[0].x, pointArr[0].y)
        if (pointArr.length > 1) {
          for (let i = 1; i < pointArr.length; i++) {
            ctx.lineTo(pointArr[i].x, pointArr[i].y)
          }
        }
        ctx.lineTo(piX, piY)
        ctx.fillStyle = 'rgba(161,195,255,0.5)' // 填充颜色
        ctx.fill() // 填充
        ctx.stroke() // 绘制
      }
    }
    
    // 存储已生成的点线
    const canvasSave = (points) => {
      ctxSave.clearRect(0, 0, ctxSave.canvas.width, ctxSave.canvas.height)
      ctxSave.beginPath()
      if (points.length > 1) {
        ctxSave.moveTo(points[0].x, points[0].y)
        for (let i = 1; i < points.length; i++) {
          ctxSave.lineTo(points[i].x, points[i].y)
          ctxSave.fillStyle = 'rgba(161,195,255,0.5)' // 填充颜色
          // ctxSave.fill()
          ctxSave.stroke() // 绘制
        }
        ctxSave.closePath()
        emit('points-change', [...points])
      }
    }
    
    // 生成画布 结束绘画
    const saveCanvas = () => {
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
      ctxSave.closePath() // 结束路径状态，结束当前路径，如果是一个未封闭的图形，会自动将首尾相连封闭起来
      ctxSave.fill() // 填充
      ctxSave.stroke() // 绘制
      emit('selection-complete', [...pointArr])
      pointArr = []
      oIndex = -1
    }
    
    // 清空选区
    const clearSelection = () => {
      const canvas = canvasRef.value
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      ctxSave.clearRect(0, 0, ctxSave.canvas.width, ctxSave.canvas.height)
      pointArr = []
      oIndex = -1
      emit('points-change', [])
    }
    
    // canvas生成圆点
    const getRandomNum = (min, max) => {
      const range = max - min
      const rand = Math.random()
      return (min + Math.round(rand * range))
    }
    
    const makearc = (ctx, x, y, r, s, e, color) => {
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) // 清空画布
      ctx.beginPath()
      ctx.fillStyle = color
      ctx.arc(x, y, r, s, e)
      ctx.fill()
    }
    
    return {
      canvasRef,
      canvasSaveRef,
      clearSelection
    }
  }
}
</script>

<style scoped>
.polygon-selector {
  position: relative;
  display: inline-block;
}

#canvas {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
}

.canvas-save {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
}

.controls {
  position: absolute;
  right: 0;
  top: 0;
  z-index: 2;
}
</style>