canvas放大缩小添加板块

146 阅读1分钟

image.png

<template>
  <div class="map-container" ref="mapDom">
    <canvas ref="cvsDom"></canvas>
    <div class="map-tool" @click="changeActionType">
      <div class="map-tool-item" :class="{ active: item.type == actionType }" :data-type="item.type" v-for="item in tools"
        v-bind:key="item.type">{{ item.label }}</div>
    </div>
    <div class="map-menu">
      <ul @click="handleMenuClick" v-if="showMenu" :style="menuStyle">
        <li data-type="copy">复制</li>
        <li data-type="delete">删除</li>
      </ul>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, watch, ref } from 'vue';
var ctx, canvas, $scale: any = ref(1), $ratio = ref(2), $rate: any = ref(null), fps = 20, timer;
var actionType = ref<String>("select") // select: 选择和操作坐标区域  move: 移动画布  addPoint: 新增点  addArea: 新增区域
var showMenu = ref<boolean>(false)
var menuStyle = ref<any>({})
var colors = ref<any>({ "1": { level: "1", color: "#ff0000" }, "2": { level: "2", color: "#ff9900" }, "3": { level: "3", color: "#f0e81a" }, "4": { level: "4", color: "#009dd9" } })
const mapDom = ref(null)
const cvsDom = ref(null)
const tools = ref([
  { type: "select", label: "选择" },
  { type: "move", label: "移动" },
  { type: "addPoint", label: "新增点" },
  { type: "addArea", label: "新增区域" },
])
const emit = defineEmits(['update']);

var areaArr = [{ "x1": 211, "y1": 129.5484, "x2": 235.1254, "y2": 169.3311, level: 2 }]
var pointArr = [{ "x": 292.4639, "y": 137.4023, level: 2 }, { "x": 127.252, "y": 169.7545, level: 1 }, { "x": 217.8381, "y": 136.9709, level: 4 }, { "x": 220.8577, "y": 142.1472, level: 3 }, { "x": 307.9929, "y": 144.7354, level: 2 }, { "x": 62.9789, "y": 198.2244, level: 4 }, { "x": 66.8612, "y": 72.2665, level: 2 }]

var arr: any = []

const props = defineProps({
  // 是否展示区域,默认为展示
  showAreas: {
    type: Boolean,
    default: true,
  },
  // 是否展示点,默认为不展示
  showPoints: {
    type: Boolean,
    default: false,
  },
  // 是否能编辑区域
  editAreas: {
    type: Boolean,
    default: false,
  },
  // 是否能编辑点
  editPoints: {
    type: Boolean,
    default: false,
  },
  // 点和区域列表
  list: {
    type: Array,
    default: []
  }
});

// 监听列表变化
watch(
  () => props.list,
  () => {
    console.log(props.list)
    arr = props.list
  },
  { immediate: true, deep: true },
);

// const setPosition = (val) => {
//   // console.log('update',val);
//   emit('setPosition', val);
// };

// initMap()

onMounted(() => {
  initMap()
});

// 初始化dom
function initMap() {
  let dom: any = mapDom.value
  let w = dom?.offsetWidth
  let h = dom?.offsetHeight
  canvas = cvsDom.value;
  canvas.width = w * $ratio.value;
  canvas.height = h * $ratio.value;
  canvas.style.width = w + "px"
  canvas.style.height = h + "px"
  ctx = canvas.getContext("2d")

  // 阻止右键菜单冒泡
  dom.oncontextmenu = (e) => { e.preventDefault() }
  // 绑定滚轮 和 鼠标按键 事件
  canvas.addEventListener("wheel", handleWheel, false)
  canvas.addEventListener("mousedown", mousedown, false)
  canvas.addEventListener("mousemove", mousemoveRecord, false)
  document.addEventListener("keydown", handleKeyDown, false)
  document.addEventListener("keyup", handleKeyUp, false)

  rendar()
}

var isMove = false, isCtrl = false, isMouseDown = false, isResize = false;
function handleKeyDown(e) {
  if (e.code == "Space") isMove = true
  if (e.key == "Control") {
    isCtrl = true
  } else {
    document.removeEventListener("keydown", handleKeyDown, false)
  }
}

function handleKeyUp(e) {
  isMove = false
  document.removeEventListener("keydown", handleKeyDown, false)
  document.addEventListener("keydown", handleKeyDown, false)
}

// 滚轮 放大缩小事件 delta 变化量为 0.1
function handleWheel(e) {
  let delta;
  if (e.wheelDelta > 0) {
    delta = 10
  } else {
    delta = -10
  }
  $scale.value = $scale.value * 100 + delta > 300 ? 3 : $scale.value * 100 + delta < 100 ? 1 : parseInt($scale.value * 100 + delta) / 100;
  bgInfo.layerX = e.layerX
  bgInfo.layerY = e.layerY
}

var moveInfo: any = {
  x1: null,
  x2: null,
  y1: null,
  y2: null
}

var moveTimer, isLongClick;
function mousedown(e) {
  if (e.which == 1) {
    isMouseDown = true
  }
  isLongClick = false;
  bgInfo.originX = Number(bgInfo.x);
  bgInfo.originY = Number(bgInfo.y);
  moveInfo.x1 = e.layerX;
  moveInfo.y1 = e.layerY;
  moveInfo.which = e.which;
  document.addEventListener("mouseup", mouseup, false)
  moveTimer = setTimeout(() => {
    isLongClick = true;
    canvas.addEventListener("mousemove", mousemove, false)
  }, 150)
}

function mousemove(e) {
  moveInfo.x2 = e.layerX;
  moveInfo.y2 = e.layerY;
}

var mouse = { x: 0, y: 0 }
function mousemoveRecord(e) {
  mouse.x = e.layerX;
  mouse.y = e.layerY;
}

function mouseup(e) {
  isMouseDown = false
  if (currentArea.isAdd) addArea()
  if (isResize) updateArea()
  if (!isLongClick) {
    handleClick(e)
    if (e.which == 3 && currentArea.selectIndex !== null) {
      showMenu.value = true
      menuStyle.value = { position: "absolute", left: e.layerX + "px", top: e.layerY + "px" }
    } else {
      showMenu.value = false
    }
  }
  clearTimeout(moveTimer)
  moveInfo = {
    x: null,
    x1: null,
    x2: null,
    y: null,
    y1: null,
    y2: null
  }
  canvas.removeEventListener("mousemove", mousemove, false)
  document.removeEventListener("mouseup", mouseup, false)
}
function handleSelection(e) {
  if (actionType.value !== "move" && !isMove && !isResize) {
    currentArea.selectIndex = null
  }
  if (actionType.value == "select" && !isMove) {
    let index = -1
    // 选中点判定
    for (let i = pointArr.length - 1; i >= 0; i--) {
      ctx.save()
      let { x, y } = mapToArea(pointArr[i].x, pointArr[i].y)
      let scale = $ratio.value * $rate.value * 0.8
      ctx.save()
      ctx.translate(x * $ratio.value, y * $ratio.value)
      ctx.beginPath()
      ctx.moveTo(0, 6 * scale)
      ctx.quadraticCurveTo(- 3 * scale, 3 * scale, - 4 * scale, - 1 * scale)
      ctx.bezierCurveTo(- 5 * scale, - 8 * scale, 5 * scale, - 8 * scale, 4 * scale, - 1 * scale)
      ctx.quadraticCurveTo(3 * scale, 3 * scale, 0, 6 * scale)
      if (ctx.isPointInPath(e.layerX * $ratio.value, e.layerY * $ratio.value)) index = i
      ctx.restore()
      if (index >= 0) {
        currentPoint.selectIndex = index
        currentArea.selectIndex = null
        break
      }
    }
    // 选中区域判定
    if (index < 0) {
      let p = areaToMap(e.layerX, e.layerY)
      for (let i = areaArr.length - 1; i >= 0; i--) {
        if (areaArr[i].x1 <= p.x && areaArr[i].x2 >= p.x && areaArr[i].y1 <= p.y && areaArr[i].y2 >= p.y) {
          index = i
          break;
        }
      }
      if (index < 0) {
        currentArea.selectIndex = null
      } else {
        let { x1, x2, y1, y2 } = areaArr[index]
        currentArea.selectIndex = index
        currentArea.x1 = x1
        currentArea.x2 = x2
        currentArea.y1 = y1
        currentArea.y2 = y2
      }
      currentArea.isAdd = false
    }
  }
}

function handleClick(e) {
  handleSelection(e)
  // 左键点击
  if (e.which == 1) {
    if (actionType.value == "addPoint" && !isMove && !isResize) {
      addPoint(e.layerX, e.layerY, "layer")
    }
  }
  // 右键点击
  if (e.which == 3) { }
}

function changeActionType(e) {
  let type = e.target.dataset.type
  if (type) actionType.value = type
}

function handleMenuClick(e) {
  let type = e.target.dataset.type
  showMenu.value = false;
  switch (type) {
    case "delete":
      if (currentPoint.selectIndex !== null) {
        pointArr.splice(currentPoint.selectIndex, 1)
      } else {
        areaArr.splice(currentArea.selectIndex, 1)
      }
      break;
  }
}

// 渲染
function rendar() {
  let w = canvas.width
  let h = canvas.height
  // move: 移动 default: 箭头 crosshair: 十字
  canvas.style.cursor = actionType.value == "move" || isMove ? (isMouseDown ? "grabbing" : "grab") : actionType.value == "addArea" ? "crosshair" : "default"
  ctx.clearRect(0, 0, w, h);
  ctx.save()
  ctx.translate(0.5, 0.5);
  darwBg(w, h)
  areaArr.forEach((item, index) => {
    drawArea(item, index)
  })
  drawArea(null, null) // 绘制新增区域
  pointArr.forEach((item, index) => {
    drawPoint(item, index)
  })
  // drawSelectArea()
  ctx.restore()
  timer = setTimeout(() => {
    rendar()
  }, 1000 / fps)
}


var currentPoint: any = { selectIndex: null }
// 新增点
function addPoint(x, y, type) {
  let p = type == "layer" ? areaToMap(x, y) : { x, y }
  pointArr.push(p)
}

// 绘制点
function drawPoint(item, index) {
  let { x, y } = mapToArea(item.x, item.y)
  let scale = $ratio.value * $rate.value * 0.8
  ctx.save()
  ctx.translate(x * $ratio.value, y * $ratio.value)
  ctx.save()
  ctx.beginPath()
  ctx.moveTo(0, 6 * scale)
  ctx.quadraticCurveTo(- 3 * scale, 3 * scale, - 4 * scale, - 1 * scale)
  ctx.bezierCurveTo(- 5 * scale, - 8 * scale, 5 * scale, - 8 * scale, 4 * scale, - 1 * scale)
  ctx.quadraticCurveTo(3 * scale, 3 * scale, 0, 6 * scale)
  ctx.closePath()
  ctx.fillStyle = colors.value[item.level] 
  ctx.lineWidth = 3
  ctx.strokeStyle = currentPoint.selectIndex == index ? "#000" : "#fff"
  ctx.globalAlpha = 0.8;
  ctx.fill()
  ctx.stroke()
  ctx.restore()
  ctx.beginPath()
  ctx.arc(0, -1.6 * scale, 2.6 * scale, 0, 2 * Math.PI)
  ctx.globalAlpha = 0.8;
  ctx.fillStyle = "#fff"
  ctx.fill()
  ctx.restore()
}

var currentArea: any = {
  isAdd: false,
  selectIndex: null,
  x1: null,
  x2: null,
  y1: null,
  y2: null
}
// 新增区域
function addArea() {
  let p1 = areaToMap(currentArea.x1, currentArea.y1)
  let p2 = areaToMap(currentArea.x2, currentArea.y2)
  let p = { x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }
  areaArr.push(p)
  currentArea = {
    isAdd: false,
    selectIndex: areaArr.length - 1,
    x1: null,
    x2: null,
    y1: null,
    y2: null
  }
}

function updateArea() {
  let p1 = areaToMap(currentArea.x1, currentArea.y1)
  let p2 = areaToMap(currentArea.x2, currentArea.y2)
  let p = { x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }
  let item = areaArr[currentArea.selectIndex]
  item = Object.assign(item, p)
  areaArr[currentArea.selectIndex] = Object.assign({}, item)
  delete currentArea["resizeType"]
  isResize = false
}

function drawArea(obj, index) {
  let { x1, x2, y1, y2 } = obj || moveInfo
  if (obj || (!obj && !isMove && actionType.value == "addArea" && moveInfo.which == 1 && moveInfo.x2 !== null)) {
    if (obj) {
      let p1 = mapToArea(x1, y1)
      let p2 = mapToArea(x2, y2)
      x1 = Number(p1.x)
      y1 = Number(p1.y)
      x2 = Number(p2.x)
      y2 = Number(p2.y)
    } else {
      currentArea.selectIndex = null
    }
    let x = x1 < x2 ? x1 : x2;
    let y = y1 < y2 ? y1 : y2;
    let w = Math.abs(x1 - x2)
    let h = Math.abs(y1 - y2)

    // 选区改变大小
    if (obj && isResize && !isMove && currentArea.selectIndex == index) {
      if (currentArea.resizeType > 0 && moveInfo.x2 !== null) {
        if (currentArea.resizeType == 1 || currentArea.resizeType == 4) {
          x += (moveInfo.x2 - moveInfo.x1)
          w -= (moveInfo.x2 - moveInfo.x1)
        } else {
          w += (moveInfo.x2 - moveInfo.x1)
        }
        if (currentArea.resizeType == 1 || currentArea.resizeType == 2) {
          y += (moveInfo.y2 - moveInfo.y1)
          h -= (moveInfo.y2 - moveInfo.y1)
        } else {
          h += (moveInfo.y2 - moveInfo.y1)
        }
      }
      // 选区改变位置
      else if (currentArea.resizeType === 0 && moveInfo.x2 !== null) {
        x += (moveInfo.x2 - moveInfo.x1)
        y += (moveInfo.y2 - moveInfo.y1)
      }
    }

    // 换算比例
    x = x * $ratio.value
    y = y * $ratio.value
    w = w * $ratio.value
    h = h * $ratio.value

    // 绘制选区
    let scale = $ratio.value * $scale.value * $rate.value
    ctx.save()
    ctx.beginPath()
    ctx.rect(x, y, w, h)
    ctx.save()
    ctx.globalAlpha = 0.8;
    ctx.fillStyle = colors.value[obj.level || "4"] 
    ctx.fill()
    ctx.restore()
    // 当前选区被选中时,框线加粗
    ctx.lineWidth = scale * (!obj || (obj && currentArea.selectIndex == index) ? 1.5 : 0.8)
    ctx.strokeStyle = "#000"
    ctx.stroke()
    ctx.restore()

    // 新增区域时记录坐标点
    if (!obj) {
      currentArea = {
        isAdd: true,
        x1, x2, y1, y2,
        selectIndex: null
      }
    }
    // 选中区域绘制改变大小的图例
    else if (currentArea.selectIndex == index) {
      let resizeType = drawAreaSelection(x, y, w, h)
      if (resizeType >= 0 && actionType.value == "select" && !isMove) calcResizeArea(resizeType, x, y, w, h)
    }
  }
}

// 绘制选中区域 操作示意
function drawAreaSelection(x, y, w, h) {
  let resizeType = -1;
  let scale = $ratio.value * $scale.value * $rate.value
  ctx.save()
  ctx.beginPath()
  ctx.translate(parseInt(x), parseInt(y))
  ctx.rect(-2 * scale, -2 * scale, 4 * scale, 4 * scale)
  ctx.fillStyle = "#fff"
  ctx.fill()
  ctx.lineWidth = scale * 1.2
  ctx.strokeStyle = "#000"
  ctx.stroke()
  if (ctx.isPointInPath(mouse.x * $ratio.value, mouse.y * $ratio.value)) resizeType = 1;
  ctx.beginPath()
  ctx.rect(-2 * scale + w, -2 * scale, 4 * scale, 4 * scale)
  ctx.fill()
  ctx.stroke()
  if (ctx.isPointInPath(mouse.x * $ratio.value, mouse.y * $ratio.value)) resizeType = 2;
  ctx.beginPath()
  ctx.rect(-2 * scale + w, -2 * scale + h, 4 * scale, 4 * scale)
  ctx.fill()
  ctx.stroke()
  if (ctx.isPointInPath(mouse.x * $ratio.value, mouse.y * $ratio.value)) resizeType = 3;
  ctx.beginPath()
  ctx.rect(-2 * scale, -2 * scale + h, 4 * scale, 4 * scale)
  ctx.fill()
  ctx.stroke()
  if (ctx.isPointInPath(mouse.x * $ratio.value, mouse.y * $ratio.value)) resizeType = 4;
  ctx.restore()

  // 如果当前鼠标坐标没在四个resize点上则判断是否在区域内部
  if (resizeType < 0) {
    ctx.save()
    ctx.beginPath()
    ctx.rect(x, y, w, h)
    if (ctx.isPointInPath(mouse.x * $ratio.value, mouse.y * $ratio.value)) resizeType = 0
    ctx.restore()
  }

  return resizeType
}

// 计算是否为改变选区大小和位置的操作
function calcResizeArea(resizeType, x, y, w, h) {
  // 改变大小 判断mouse的位置是否
  currentArea.resizeType = resizeType;
  if (resizeType) canvas.style.cursor = resizeType % 2 == 1 ? "nw-resize" : "sw-resize"
  if (isMouseDown && !isResize) isResize = true
  else if (isMouseDown && isResize) {
    currentArea.x1 = x / $ratio.value
    currentArea.y1 = y / $ratio.value
    currentArea.x2 = (x + w) / $ratio.value
    currentArea.y2 = (y + h) / $ratio.value
  }

}

// function drawSelectArea() {
//   if (actionType.value == "select" && !isMove && moveInfo.which == 1 && moveInfo.x2 !== null) {
//     let { x1, x2, y1, y2 } = moveInfo
//     let x = x1 < x2 ? x1 : x2;
//     let y = y1 < y2 ? y1 : y2;
//     let w = Math.abs(x1 - x2)
//     let h = Math.abs(y1 - y2)
//     ctx.save()
//     ctx.beginPath()
//     ctx.rect(x * $ratio.value, y * $ratio.value, w * $ratio.value, h * $ratio.value)
//     ctx.save()
//     ctx.globalAlpha = 0.1;
//     ctx.fillStyle = "#6894cb"
//     ctx.fill()
//     ctx.restore()
//     ctx.setLineDash([4 * $ratio.value, 4 * $ratio.value])
//     ctx.lineWidth = $ratio.value * 1
//     ctx.strokeStyle = "#000000"
//     ctx.stroke()
//     ctx.restore()
//   }
// }

var bgInfo: any = {
  x: null, // 当前背景偏移
  y: null,
  originScale: 1, // 选中前scale值
  originX: null,  // 选中前背景偏移
  originY: null,
  src: "/resource/img/map_img.jpg",  // 背景图片地址
  img: null,   // 背景图片对象
  load: false  // 背景是否加载
}
bgInfo.img = new Image();
bgInfo.img.src = bgInfo.src.toString()
bgInfo.img.onload = () => {
  bgInfo.load = true
}
function darwBg(w, h) {
  if (!bgInfo.load) return
  let img = bgInfo.img
  let rate1 = w / h;
  let rate2 = img.width / img.height;
  $rate.value = rate1 > rate2 ? (w / img.width) : (h / img.height);
  let width = img.width * $rate.value
  let height = img.height * $rate.value
  let x, y;
  if (bgInfo.x === null) {
    // 初始化居中显示
    x = (w - width) / 2
    y = (h - height) / 2
  }
  else if ($scale.value == bgInfo.originScale) {
    if ((isMove || actionType.value == "move") && moveInfo.x2 !== null) {
      // 移动画布
      x = bgInfo.originX + (moveInfo.x2 - moveInfo.x1)
      y = bgInfo.originY + (moveInfo.y2 - moveInfo.y1)
    } else {
      // 未改变scale
      x = bgInfo.x
      y = bgInfo.y
    }
  } else {
    let delta = parseInt((bgInfo.originScale || 1) * 100 - $scale.value * 100) / 100
    x = bgInfo.x + (bgInfo.layerX) * delta
    y = bgInfo.y + (bgInfo.layerY) * delta
  }

  x = x > 0 ? 0 : x + width * $scale.value < w ? w - width * $scale.value : x;
  y = y > 0 ? 0 : y + height * $scale.value < h ? h - height * $scale.value : y;

  bgInfo.x = Number(x)
  bgInfo.y = Number(y)
  bgInfo.originScale = Number($scale.value)
  ctx.drawImage(img, x, y, width * $scale.value, height * $scale.value)
}

// 坐标系转换 可视区域坐标转地图坐标
function areaToMap(layerX, layerY) {
  let mapX = (layerX * $ratio.value - bgInfo.x) / ($scale.value * $ratio.value) / $rate.value
  let mapY = (layerY * $ratio.value - bgInfo.y) / ($scale.value * $ratio.value) / $rate.value
  mapX = parseInt(mapX * 10000) / 10000
  mapY = parseInt(mapY * 10000) / 10000
  return { x: mapX, y: mapY }
}
// 坐标系转换 地图坐标坐标转可视区域坐标
function mapToArea(mapX, mapY) {
  let layerX = (mapX * ($scale.value * $ratio.value * $rate.value) + bgInfo.x) / $ratio.value
  let layerY = (mapY * ($scale.value * $ratio.value * $rate.value) + bgInfo.y) / $ratio.value
  layerX = parseInt(layerX * 10000) / 10000
  layerY = parseInt(layerY * 10000) / 10000
  return { x: layerX, y: layerY }
}

</script>

<style lang="less" scoped>
.map-container {
  position: relative;
  width: 100%;
  height: 100%;
}

.map-tool {
  position: absolute;
  right: 6px;
  bottom: 6px;
  user-select: none;
}

.map-tool-item {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 40px;
  height: 40px;
  line-height: 1.2;
  margin-bottom: 6px;
  text-align: center;
  padding: 3px;
  box-sizing: border-box;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
}

.map-tool-item.active {
  background: #2a50ec;
  color: #fff;
}

.map-tool-item:last-child {
  margin-bottom: 0;
}
<<<<<<< .mine
</style>
||||||| .r12116
</style>=======

.map-select-area-resize {
  position: absolute;
  width: 10px;
  height: 10px;
  border: 2px solid #000;
  background: rgba(255, 255, 255, 1);
}

.map-select-area-resize.point-1 {
  top: -4px;
  left: -4px;
}

.map-select-area-resize.point-2 {
  top: -4px;
  right: -4px;
}

.map-select-area-resize.point-3 {
  bottom: -4px;
  left: -4px;
}

.map-select-area-resize.point-4 {
  bottom: -4px;
  right: -4px;
}

.map-menu ul {
  width: 100px;
  padding: 2px;
  background: #fff;
}

.map-menu ul li {
  padding: 2px 10px;
  border-bottom: 1px solid #eee;
  cursor: pointer;
}

.map-menu ul li:hover {
  background: rgba(108, 167, 216, .3);
}

.map-menu ul li:last-child {
  border-bottom: none;
}
</style>>>>>>>> .r12122

image.png

<template>
  <div class="map-container" ref="mapDom">
    <canvas ref="cvsDom"></canvas>
    <div class="map-tool" @click="changeActionType">
      <div class="map-tool-item" :class="{ active: item.type == actionType }" :data-type="item.type" v-for="item in tools"
        v-bind:key="item.type" v-show="item.isShow === undefined || (item.isShow && item.isShow())">{{ item.label }}</div>
    </div>
    <div class="map-menu">
      <ul @click="handleMenuClick" v-if="showMenu" :style="menuStyle">
        <!-- <li data-type="copy">复制</li> -->
        <li data-type="delete">删除</li>
      </ul>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted, watch, ref } from 'vue';
var ctx, canvas, $scale: any = ref(1), $ratio = ref(2), $rate: any = ref(null), fps = 20, timer;
var actionType = ref<String>("select") // select: 选择和操作坐标区域  move: 移动画布  addPoint: 新增点  addArea: 新增区域
var showMenu = ref<boolean>(false)
var menuStyle = ref<any>({})
const colors = {
  "3": { level: "3", color: "#ff0000" },
  "2": { level: "2", color: "#ff8f05" },
  "1": { level: "1", color: "#ffdf00" },
  "0": { level: "0", color: "#009dd9" }
}
var color: String = "#009dd9"
const mapDom = ref(null)
const cvsDom = ref(null)
const tools = ref([
  { type: "select", label: "选择" },
  { type: "move", label: "移动" },
  { type: "addPoint", label: "新增点", isShow: () => { return props.editPoints && props.addPoint } },
  { type: "addArea", label: "新增区域", isShow: () => { return props.editAreas && props.addArea } },
])
const emit = defineEmits(['choose', 'update', 'delete', 'addArea']);

var areaArr = []
var pointArr = []


const props = defineProps({
  // 是否能编辑区域
  editAreas: {
    type: Boolean,
    default: false,
  },
  addArea: {
    type: Boolean,
    default: true,
  },
  // 是否能编辑点
  editPoints: {
    type: Boolean,
    default: false,
  },
  addPoint: {
    type: Boolean,
    default: true,
  },
  // 点和区域列表
  list: {
    type: Array,
    default: []
  },
  // 风险等级
  level: {
    type: String,
    default: "0",
  }
});

// 监听列表变化
watch(
  () => props.list,
  () => {
    areaArr = []
    pointArr = []
    for (let i = 0; i < props.list.length; i++) {
      let item: any = Object.assign({}, props.list[i])
      if (item._type == "area") {
        areaArr.push(item)
      } else if (item._type == "point") {
        pointArr.push(item)
      }
    }
  },
  { immediate: true, deep: true },
);

watch(
  () => props.level,
  () => {
    let level = (props.level || "").toString()
    color = props.level ? colors[level].color.toString() : "#666"
    if (currentArea.selectIndex || currentArea.selectIndex === 0) {
      areaArr[currentArea.selectIndex].level = level
      emit("update", "area", areaArr[currentArea.selectIndex])
    }
    if (currentPoint.selectIndex || currentPoint.selectIndex === 0) {
      pointArr[currentPoint.selectIndex].level = level
      emit("update", "point", pointArr[currentArea.selectIndex])
    }
  },
  { deep: true },
);

// const setPosition = (val) => {
//   // console.log('update',val);
//   emit('setPosition', val);
// };

// initMap()
onMounted(() => {
  initMap()
});

onUnmounted(() => {
  // 组件销毁
  clearTimeout(timer)
  clearTimeout(moveTimer)
  canvas.removeEventListener("wheel", handleWheel, false)
  canvas.removeEventListener("mousedown", mousedown, false)
  canvas.removeEventListener("mousemove", mousemoveRecord, false)
  canvas.removeEventListener("mousemove", mousemove, false)
  document.removeEventListener("mouseup", mouseup, false)
  document.removeEventListener("keydown", handleKeyDown, false)
  document.removeEventListener("keyup", handleKeyUp, false)
})


var $dom: any;
// 初始化dom
var isMove = false, isCtrl = false, isMouseDown = false, isResize = false, moveTimer, isLongClick;
function initMap() {
  $dom = mapDom.value
  canvas = cvsDom.value;
  isMove = false;
  isCtrl = false;
  isMouseDown = false;
  isResize = false;
  // let w = dom?.offsetWidth - 2
  // let h = dom?.offsetHeight - 2
  // canvas.width = w * $ratio.value;
  // canvas.height = h * $ratio.value;
  // canvas.style.width = w + "px"
  // canvas.style.height = h + "px"
  // ctx = canvas.getContext("2d")

  // 阻止右键菜单冒泡
  $dom.oncontextmenu = (e) => { e.preventDefault() }
  // 绑定滚轮 和 鼠标按键 事件
  canvas.addEventListener("wheel", handleWheel, false)
  canvas.addEventListener("mousedown", mousedown, false)
  canvas.addEventListener("mousemove", mousemoveRecord, false)
  document.addEventListener("keydown", handleKeyDown, false)
  document.addEventListener("keyup", handleKeyUp, false)

  rendar()
}

function getSize() {
  let w = $dom?.offsetWidth - 2
  let h = $dom?.offsetHeight - 2
  canvas.width = w * $ratio.value;
  canvas.height = h * $ratio.value;
  canvas.style.width = w + "px"
  canvas.style.height = h + "px"
  ctx = canvas.getContext("2d")
}

function handleKeyDown(e) {
  if (e.code == "Space") isMove = true
  if (e.key == "Control") {
    isCtrl = true
  } else {
    document.removeEventListener("keydown", handleKeyDown, false)
  }
  if (e.key == "Delete") {
    deleteItem()
  }
}

function handleKeyUp(e) {
  isMove = false
  document.removeEventListener("keydown", handleKeyDown, false)
  document.addEventListener("keydown", handleKeyDown, false)
}

// 滚轮 放大缩小事件 delta 变化量为 0.1
function handleWheel(e) {
  let delta;
  if (e.wheelDelta > 0) {
    delta = 10
  } else {
    delta = -10
  }
  $scale.value = $scale.value * 100 + delta > 300 ? 3 : $scale.value * 100 + delta < 100 ? 1 : parseInt($scale.value * 100 + delta) / 100;
  bgInfo.layerX = e.layerX
  bgInfo.layerY = e.layerY
}

var moveInfo: any = {
  x1: null,
  x2: null,
  y1: null,
  y2: null
}

function mousedown(e) {
  if (e.which == 1) {
    isMouseDown = true
  }
  isLongClick = false;
  bgInfo.originX = Number(bgInfo.x);
  bgInfo.originY = Number(bgInfo.y);
  moveInfo.x1 = e.layerX;
  moveInfo.y1 = e.layerY;
  moveInfo.x2 = null
  moveInfo.y2 = null
  moveInfo.which = e.which;
  document.addEventListener("mouseup", mouseup, false)
  moveTimer = setTimeout(() => {
    isLongClick = true;
    canvas.addEventListener("mousemove", mousemove, false)
  }, 150)
}

function mousemove(e) {
  moveInfo.x2 = e.layerX;
  moveInfo.y2 = e.layerY;
}

var mouse = { x: 0, y: 0 }
function mousemoveRecord(e) {
  mouse.x = e.layerX;
  mouse.y = e.layerY;
}

function mouseup(e) {
  isMouseDown = false
  if (isResize && isLongClick && currentArea.selectIndex !== null) updateArea()
  if (isResize && isLongClick && currentPoint.selectIndex !== null) updatePoint()
  if (!isLongClick) {
    showMenu.value = false
    handleClick(e)
  }
  clearTimeout(moveTimer)
  moveInfo = {
    x: null,
    x1: null,
    x2: null,
    y: null,
    y1: null,
    y2: null
  }
  isResize = false
  canvas.removeEventListener("mousemove", mousemove, false)
  document.removeEventListener("mouseup", mouseup, false)
}
function handleSelection(e) {
  if (actionType.value !== "move" && !isMove && !isResize) {
    currentArea.selectIndex = null
  }
  if (actionType.value == "select" && !isMove) {
    let index = -1
    // 选中点判定
    for (let i = pointArr.length - 1; i >= 0; i--) {
      if (!pointArr[i].disable) {
        ctx.save()
        let { x, y } = mapToArea(pointArr[i].x, pointArr[i].y)
        let scale = $ratio.value * $rate.value * 0.8
        ctx.save()
        ctx.translate(x * $ratio.value, y * $ratio.value)
        ctx.beginPath()
        ctx.moveTo(0, 6 * scale)
        ctx.quadraticCurveTo(- 3 * scale, 3 * scale, - 4 * scale, - 1 * scale)
        ctx.bezierCurveTo(- 5 * scale, - 8 * scale, 5 * scale, - 8 * scale, 4 * scale, - 1 * scale)
        ctx.quadraticCurveTo(3 * scale, 3 * scale, 0, 6 * scale)
        if (ctx.isPointInPath(e.layerX * $ratio.value, e.layerY * $ratio.value)) index = i
        ctx.restore()
        if (index >= 0) {
          currentArea.selectIndex = null
          currentPoint.selectIndex = index
          emit("choose", "point", pointArr[index], { x: e.layerX, y: e.layerY })
          break
        }
      }
    }
    // 选中区域判定
    if (index < 0) {
      currentPoint.selectIndex = null
      for (let i = areaArr.length - 1; i >= 0; i--) {
        if (!areaArr[i].disable) {
          let arr = areaArr[i].arr;
          ctx.save()
          ctx.beginPath()
          for (let i = 0; i <= arr.length; i++) {
            let _i = i == arr.length ? 0 : i;
            let { x, y } = mapToArea(arr[_i].x, arr[_i].y)
            x = x * $ratio.value
            y = y * $ratio.value
            ctx[i ? "lineTo" : "moveTo"](x, y)
          }
          if (ctx.isPointInPath(e.layerX * $ratio.value, e.layerY * $ratio.value)) {
            index = i
            emit("choose", "area", areaArr[index], { x: e.layerX, y: e.layerY })
          }
          ctx.restore()
        }
      }
      if (index < 0) {
        currentArea.selectIndex = null
      } else {
        currentArea.selectIndex = index
      }
      currentArea.isAdd = false
    }
  }
}

function handleClick(e) {
  handleSelection(e)
  // 左键点击
  if (e.which == 1) {
    if (actionType.value == "addPoint" && !isMove && !isResize) {
      addPoint(e.layerX, e.layerY, "layer")
      actionType.value = "select"
    }
    if (actionType.value == "addArea" && !isMove && !isResize) {
      if (!currentArea.arr) currentArea.arr = []
      let isOver = false;
      currentArea.isAdd = true
      let { x, y } = areaToMap(e.layerX, e.layerY)
      if (currentArea.arr.length) {
        currentArea.arr.forEach(p => {
          if (Math.abs(x - p.x) < 10 && Math.abs(y - p.y) < 10) isOver = true
        })
      }
      if (isOver) {
        if (currentArea.arr.length >= 3) { addArea(currentArea.arr) }
        currentArea.arr = []
      }
      else {
        currentArea.arr.push({ x, y })
      }
    }
  }
  // 右键点击
  if (e.which == 3) {
    // 新增区域时,右键结束新增操作
    if (actionType.value == "addArea" && !isMove && !isResize) {
      if (currentArea.arr.length >= 3) { addArea(currentArea.arr) } else { currentArea.arr = [] }
    }
    // 右键打开删除操作弹窗
    else if (currentArea.selectIndex !== null) {
      showMenu.value = true
      menuStyle.value = { position: "absolute", left: e.layerX + "px", top: e.layerY + "px" }
    }
    if (actionType.value == "select" && currentPoint.selectIndex !== null) {
      showMenu.value = true
      menuStyle.value = { position: "absolute", left: e.layerX + "px", top: e.layerY + "px" }
    }
  }
}

function changeActionType(e) {
  let type = e.target.dataset.type
  if (type) actionType.value = type
  if (type == "addArea" && currentArea.isAdd && currentArea.arr.length >= 3) addArea(currentArea.arr)
  if (type == "addArea") currentArea.arr = []
}

function handleMenuClick(e) {
  let type = e.target.dataset.type
  showMenu.value = false;
  switch (type) {
    case "delete":
      deleteItem()
      break;
  }
}

function deleteItem() {
  if (currentPoint.selectIndex !== null) {
    let item = pointArr.splice(currentPoint.selectIndex, 1)
    currentPoint.selectIndex = null
    emit("delete", item.id)
  } else {
    let item = areaArr.splice(currentArea.selectIndex, 1)
    currentArea.selectIndex = null
    emit("delete", item.id)
  }
}

// 渲染
function rendar() {
  getSize()
  let w = canvas.width
  let h = canvas.height
  // move: 移动 default: 箭头 crosshair: 十字
  canvas.style.cursor = actionType.value == "move" || isMove ? (isMouseDown ? "grabbing" : "grab") : actionType.value == "addArea" ? "crosshair" : "default"
  ctx.clearRect(0, 0, w, h);
  ctx.save()
  ctx.translate(0.5, 0.5);
  darwBg(w, h)
  areaArr.forEach((item, index) => {
    drawArea(item, index)
  })
  if (currentArea.isAdd) drawArea(null, null) // 绘制新增区域
  pointArr.forEach((item, index) => {
    drawPoint(item, index)
  })
  // drawSelectArea()
  ctx.restore()
  timer = setTimeout(() => {
    rendar()
  }, 1000 / fps)
}


var currentPoint: any = { selectIndex: null }
// 新增点
function addPoint(x, y, type) {
  let p = type == "layer" ? areaToMap(x, y) : { x, y }
  p.level = 2
  pointArr.push(p)
}

function updatePoint() {
  let { x, y } = areaToMap(currentPoint.x, currentPoint.y)
  pointArr[currentPoint.selectIndex].x = x
  pointArr[currentPoint.selectIndex].y = y
}

// 绘制点
function drawPoint(item, index) {
  let { x, y } = mapToArea(item.x, item.y)
  let scale = $ratio.value * $rate.value * (1 + ($scale.value - 1) / 3)
  let isChoose = currentPoint.selectIndex == index
  if (isChoose && actionType.value == "select" && isResize && moveInfo.x2 !== null) {
    x += (moveInfo.x2 - moveInfo.x1)
    y += (moveInfo.y2 - moveInfo.y1)
    currentPoint.x = Number(x)
    currentPoint.y = Number(y)
  }
  ctx.save()
  ctx.globalAlpha = isChoose ? 1 : 0.9;
  ctx.translate(x * $ratio.value, y * $ratio.value)
  ctx.save()
  ctx.beginPath()
  ctx.moveTo(0, 6 * scale)
  ctx.quadraticCurveTo(- 3 * scale, 3 * scale, - 4 * scale, - 1 * scale)
  ctx.bezierCurveTo(- 5 * scale, - 8 * scale, 5 * scale, - 8 * scale, 4 * scale, - 1 * scale)
  ctx.quadraticCurveTo(3 * scale, 3 * scale, 0, 6 * scale)
  let isInPath = ctx.isPointInPath(mouse.x * $ratio.value, mouse.y * $ratio.value)
  if (actionType.value == "select" && isChoose && isInPath) {
    canvas.style.cursor = isMouseDown ? "grabbing" : "grab"
    if (isMouseDown && !isResize) isResize = true
  }
  ctx.closePath()
  ctx.fillStyle = item.level !== undefined ? colors[item.level].color : color;
  ctx.lineWidth = isChoose ? 5 : 3;
  ctx.strokeStyle = isChoose ? "#000" : "#fff"
  ctx.globalAlpha = 1;
  ctx.fill()
  ctx.globalAlpha = isChoose ? 0.7 : 1;
  ctx.stroke()
  ctx.restore()
  ctx.beginPath()
  ctx.arc(0, -1.6 * scale, 2.2 * scale, 0, 2 * Math.PI)
  ctx.fillStyle = "#fff"
  ctx.globalAlpha = isChoose ? 0.8 : 0.5;
  ctx.fill()
  ctx.restore()
}

var currentArea: any = {
  isAdd: false,
  selectIndex: null,
  resizeType: null,
  arr: [],
  x: null,
  y: null,
}
// 新增区域
function addArea(arr) {
  areaArr.push({ arr, level: props.level || 4 })
  currentArea.arr = []
  currentArea.selectIndex = areaArr.length - 1
  actionType.value = "select"
  emit("addArea", arr)
}

function updateArea() {
  if (currentArea.resizeType >= 0 && currentArea.x !== null) {
    areaArr[currentArea.selectIndex].arr[currentArea.resizeType].x = currentArea.x
    areaArr[currentArea.selectIndex].arr[currentArea.resizeType].y = currentArea.y
  } else if (currentArea.resizeType == -1) {
    areaArr[currentArea.selectIndex].arr = currentArea.arr
  }
  delete currentArea["resizeType"]
  isResize = false
  emit("update", "area", areaArr[currentArea.selectIndex])
}

function drawArea(obj, index) {
  if (!obj && !currentArea.arr.length) return
  let { arr, level } = obj || currentArea;
  arr = arr.map((p) => {
    let { x, y } = mapToArea(p.x, p.y)
    x = x * $ratio.value
    y = y * $ratio.value
    return { x, y }
  })

  let minX, maxX, minY, maxY;
  let scale = $ratio.value * $scale.value * $rate.value
  ctx.save()
  ctx.beginPath()
  for (let i = 0; i < arr.length; i++) {
    // 选区改变大小
    if (obj && !obj._lock && isResize && !isMove && currentArea.selectIndex == index && moveInfo.x2 !== null && (currentArea.resizeType === i || currentArea.resizeType == -1)) {
      arr[i].x += (moveInfo.x2 - moveInfo.x1) * $ratio.value
      arr[i].y += (moveInfo.y2 - moveInfo.y1) * $ratio.value
    }

    if (minX === undefined) { minX = Number(arr[i].x) }
    else if (minX > arr[i].x) { minX = Number(arr[i].x) }
    if (maxX === undefined) { maxX = Number(arr[i].x) }
    else if (maxX < arr[i].x) { maxX = Number(arr[i].x) }
    if (minY === undefined) { minY = Number(arr[i].y) }
    else if (minY > arr[i].y) { minY = Number(arr[i].y) }
    if (maxY === undefined) { maxY = Number(arr[i].y) }
    else if (maxY < arr[i].y) { maxY = Number(arr[i].y) }

    ctx[i ? "lineTo" : "moveTo"](arr[i].x, arr[i].y)
  }
  if (obj) ctx.lineTo(arr[0].x, arr[0].y)
  ctx.save()
  ctx.globalAlpha = !obj || (obj && currentArea.selectIndex == index) ? 0.6 : 0.5;
  ctx.fillStyle = level !== undefined ? colors[level].color : color;
  ctx.fill()
  ctx.globalAlpha = 1;
  ctx.restore()
  // 当前选区被选中时,框线加粗
  ctx.lineWidth = scale * (!obj || (obj && currentArea.selectIndex == index) ? 1.2 : 0.8)
  ctx.strokeStyle = "#000"
  ctx.stroke()
  ctx.restore()

  if (obj && obj._name && arr.length >= 3) {
    let fontSize = 6.2
    ctx.save()
    ctx.textBaseline = "middle"
    ctx.textAlign = "center"
    ctx.font = fontSize * scale + "px 黑体 bold"
    ctx.fillStyle = "#fff"
    ctx.globalAlpha = obj && currentArea.selectIndex == index ? 1 : 0.8;
    drawText(obj._name, minX, maxX, minY, maxY, fontSize)
    ctx.restore()
  }


  if (!obj) {
    drawAreaSelection(arr)
  }
  // 选中区域绘制改变大小的图例
  else if (currentArea.selectIndex == index && !obj._lock) {
    let resizeType = drawAreaSelection(arr)
    if (resizeType !== null && actionType.value == "select" && !isMove) calcResizeArea(resizeType, arr)
  }

  return
}


function drawText(text, x1, x2, y1, y2, font) {
  let scale = $ratio.value * $scale.value * $rate.value
  let arr = text.split("")
  let line = [[]], line_index = 0;
  arr.forEach(item => {
    if (line[line_index].length) {
      let text = line[line_index].join("") + item
      let w = ctx.measureText(text).width
      if (w < x2 - x1) {
        line[line_index].push(item)
      } else {
        line.push([item])
        line_index++
      }
    } else {
      line[line_index].push(item)
    }
  })

  let lineHeight = font * scale;
  ctx.translate(x1, y1)
  let w = x2 - x1;
  let h = y2 - y1;
  let deltaY = -(line.length - 1) * lineHeight / 2
  line.forEach((item, index) => {
    let str = item.join("")
    ctx.fillText(str, w / 2, h / 2 + lineHeight * index + deltaY, w - 2 * scale)
  })
}

// 绘制选中区域 操作示意
function drawAreaSelection(arr) {
  let resizeType: any = -1;
  let scale = $ratio.value * $scale.value * $rate.value

  for (let i = 0; i < arr.length; i++) {
    ctx.save()
    ctx.translate(parseInt(arr[i].x), parseInt(arr[i].y))
    ctx.beginPath()
    ctx.rect(-2 * scale, -2 * scale, 4 * scale, 4 * scale)
    ctx.fillStyle = "#fff"
    ctx.fill()
    ctx.lineWidth = scale * 1.2
    ctx.strokeStyle = "#000"
    ctx.stroke()
    if (ctx.isPointInPath(mouse.x * $ratio.value, mouse.y * $ratio.value)) resizeType = i;
    ctx.restore()
  }

  if (resizeType < 0) {
    ctx.save()
    ctx.beginPath()
    for (let i = 0; i < arr.length; i++) {
      ctx[i ? "lineTo" : "moveTo"](arr[i].x, arr[i].y)
    }
    ctx.closePath()
    if (!ctx.isPointInPath(mouse.x * $ratio.value, mouse.y * $ratio.value)) resizeType = null;
    ctx.restore()
  }
  return resizeType
}

// 计算是否为改变选区大小和位置的操作
function calcResizeArea(resizeType, arr) {
  // 改变大小 判断mouse的位置是否
  canvas.style.cursor = "move"
  if (isMouseDown && !isResize) {
    isResize = true
    currentArea.resizeType = resizeType;
  }
  else if (isMouseDown && isResize && resizeType >= 0) {
    let { x, y } = areaToMap(arr[resizeType].x / $ratio.value, arr[resizeType].y / $ratio.value);
    currentArea.x = x
    currentArea.y = y
  } else if (isMouseDown && isResize && resizeType == -1) {
    let new_arr = arr.map(item => {
      let { x, y } = areaToMap(item.x / $ratio.value, item.y / $ratio.value);
      return { x, y }
    })
    currentArea.arr = new_arr
  }

}

// function drawSelectArea() {
//   if (actionType.value == "select" && !isMove && moveInfo.which == 1 && moveInfo.x2 !== null) {
//     let { x1, x2, y1, y2 } = moveInfo
//     let x = x1 < x2 ? x1 : x2;
//     let y = y1 < y2 ? y1 : y2;
//     let w = Math.abs(x1 - x2)
//     let h = Math.abs(y1 - y2)
//     ctx.save()
//     ctx.beginPath()
//     ctx.rect(x * $ratio.value, y * $ratio.value, w * $ratio.value, h * $ratio.value)
//     ctx.save()
//     ctx.globalAlpha = 0.1;
//     ctx.fillStyle = "#6894cb"
//     ctx.fill()
//     ctx.restore()
//     ctx.setLineDash([4 * $ratio.value, 4 * $ratio.value])
//     ctx.lineWidth = $ratio.value * 1
//     ctx.strokeStyle = "#000000"
//     ctx.stroke()
//     ctx.restore()
//   }
// }

var bgInfo: any = {
  x: null, // 当前背景偏移
  y: null,
  originScale: 1, // 选中前scale值
  originX: null,  // 选中前背景偏移
  originY: null,
  src: "/resource/img/map_img.jpg",  // 背景图片地址
  img: null,   // 背景图片对象
  load: false  // 背景是否加载
}
bgInfo.img = new Image();
bgInfo.img.src = bgInfo.src.toString()
bgInfo.img.onload = () => {
  bgInfo.load = true
}
function darwBg(w, h) {
  if (!bgInfo.load) return
  let img = bgInfo.img
  let rate1 = w / h;
  let rate2 = img.width / img.height;
  $rate.value = rate1 > rate2 ? (w / img.width) : (h / img.height);
  let width = img.width * $rate.value
  let height = img.height * $rate.value
  let x, y;
  if (bgInfo.x === null) {
    // 初始化居中显示
    x = (w - width) / 2
    y = (h - height) / 2
  }
  else if ($scale.value == bgInfo.originScale) {
    if ((isMove || actionType.value == "move") && moveInfo.x2 !== null) {
      // 移动画布
      x = bgInfo.originX + (moveInfo.x2 - moveInfo.x1)
      y = bgInfo.originY + (moveInfo.y2 - moveInfo.y1)
    } else {
      // 未改变scale
      x = bgInfo.x
      y = bgInfo.y
    }
  } else {
    let delta = parseInt((bgInfo.originScale || 1) * 100 - $scale.value * 100) / 100
    x = bgInfo.x + bgInfo.layerX * $ratio.value * delta
    y = bgInfo.y + bgInfo.layerY * $ratio.value * delta
  }

  x = x > 0 ? 0 : x + width * $scale.value < w ? w - width * $scale.value : x;
  y = y > 0 ? 0 : y + height * $scale.value < h ? h - height * $scale.value : y;

  bgInfo.x = Number(x)
  bgInfo.y = Number(y)
  bgInfo.originScale = Number($scale.value)
  ctx.drawImage(img, x, y, width * $scale.value, height * $scale.value)
}

// 坐标系转换 可视区域坐标转地图坐标
function areaToMap(layerX, layerY) {
  let mapX = (layerX * $ratio.value - bgInfo.x) / ($scale.value * $ratio.value) / $rate.value
  let mapY = (layerY * $ratio.value - bgInfo.y) / ($scale.value * $ratio.value) / $rate.value
  mapX = parseInt(mapX * 10000) / 10000
  mapY = parseInt(mapY * 10000) / 10000
  return { x: mapX, y: mapY }
}
// 坐标系转换 地图坐标坐标转可视区域坐标
function mapToArea(mapX, mapY) {
  let layerX = (mapX * ($scale.value * $ratio.value * $rate.value) + bgInfo.x) / $ratio.value
  let layerY = (mapY * ($scale.value * $ratio.value * $rate.value) + bgInfo.y) / $ratio.value
  layerX = parseInt(layerX * 10000) / 10000
  layerY = parseInt(layerY * 10000) / 10000
  return { x: layerX, y: layerY }
}

</script>

<style lang="less" scoped>
.map-container {
  position: relative;
  width: 100%;
  height: 100%;
}

.map-tool {
  position: absolute;
  right: 6px;
  bottom: 6px;
  user-select: none;
}

.map-tool-item {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 40px;
  height: 40px;
  line-height: 1.2;
  margin-bottom: 6px;
  text-align: center;
  padding: 3px;
  box-sizing: border-box;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
}

.map-tool-item.active {
  background: #2a50ec;
  color: #fff;
}

.map-tool-item:last-child {
  margin-bottom: 0;
}

.map-select-area-resize {
  position: absolute;
  width: 10px;
  height: 10px;
  border: 2px solid #000;
  background: rgba(255, 255, 255, 1);
}

.map-select-area-resize.point-1 {
  top: -4px;
  left: -4px;
}

.map-select-area-resize.point-2 {
  top: -4px;
  right: -4px;
}

.map-select-area-resize.point-3 {
  bottom: -4px;
  left: -4px;
}

.map-select-area-resize.point-4 {
  bottom: -4px;
  right: -4px;
}

.map-menu ul {
  width: 100px;
  padding: 2px;
  background: #fff;
}

.map-menu ul li {
  padding: 2px 10px;
  border-bottom: 1px solid #eee;
  cursor: pointer;
}

.map-menu ul li:hover {
  background: rgba(108, 167, 216, .3);
}

.map-menu ul li:last-child {
  border-bottom: none;
}
</style>

html

<template>
  <div class="canvas_container">
    <j-map :list="list" @choose="choose"></j-map>
  </div>
</template>

<script lang="ts">
export default defineComponent({
  name: 'riskDistributionMap'
});
</script>
<script lang="ts" setup>
import { defineComponent, ref } from 'vue';
import jMap from "/@/views/doublecontrol/components/map.vue"
import { controlRiskAreaListData } from '/@/api/doublecontrol/riskarea/controlRiskArea';

const list = ref<any>([])
var areaArr: any = []

getRiskAreaList(1)

// 获取所有区域
function getRiskAreaList(pageNo: 1) {
  let pageSize = 50
  if (pageNo == 1) areaArr = []
  controlRiskAreaListData({ pageNo, pageSize }).then(res => {
    if (res.list && res.list.length) {
      res.list.forEach(item => {
        item.level = item.riskLevel
        item._type = "area"
        item.arr = JSON.parse(item.regionalDivision) || null
        item._lock = true
        item._name = item.areaUnitName.toString()
        areaArr.push(Object.assign({}, item))
      })
      list.value = [...areaArr]

      if (res.list.length == pageSize) getRiskAreaList(pageNo + 1)
    }
    console.log(list);
    
  })
}

const choose = (_type, item) => {
  console.log(_type, item)
}

</script>


<style lang="less" scoped>
.canvas_container {
  width: 1200px;
  height: 760px;
  border: 1px solid #aaa;
  margin: 20px auto 0;
}
</style>