canvas图上绘制边框,可放大缩小,可拖动

 



<template>
  <div>
    按下左键拖动绘制,按住绿色圆点拖动修改位置,按下delete删除
    <!-- <button id="checkData">查看数据</button> -->
  </div>
  <div id="dataView"></div>
  <div id="canvasBox" class="canvasBox" style="position: relative">
    <canvas id="myCanvas" width="1000" height="800" style="background: #ccc"></canvas>
  </div>
</template>
<script setup >
import { DeleteOutlined } from "@ant-design/icons-vue"
import { onMounted, ref, onBeforeUnmount, watch } from "vue"
const props = defineProps({
  imgSrc: { type: String, default: "" },
  mode: { type: String, default: "" },
  toCanvasData: { type: Array, default: () => [] }
})

let canvasBox
let img = new Image()
let datas = [] //绘制的数据
let editTarget = null
let start = null
let frames = []
const imageUrl = ref(null)

watch(
  () => props.imgSrc,
  (newVal) => {
    if (newVal) {
      imageUrl.value = newVal
      img.src = newVal
      // 切换图片时清空数据和框
      clearData()
    }
  },
  { deep: true, immediate: true }
)

watch(
  () => [props.toCanvasData, props.mode],
  (newVal) => {
    if (newVal[0] && newVal[1] === "edit") {
      setTimeout(() => {
        clearData()
        loadData(JSON.parse(JSON.stringify(newVal[0])))
      }, 1000)
    }
  },
  { deep: true, immediate: true }
)
onBeforeUnmount(() => {
  // 组件销毁前移除事件监听器,防止内存泄漏
  document.removeEventListener("keydown", handleKeyDown)
  // 在组件销毁前清除画布对象
     clearData()
})

let scale = 1
let mycanvas = null
onMounted(() => {
  canvasBox = document.getElementById("canvasBox")
  let c = document.getElementById("myCanvas")
  mycanvas = c
  let ctx = c.getContext("2d")
  if (!img.src) return
  img.onload = function (e) {
    console.log("img onload")
    drawImg(this)
  }
  // 图片宽高,根据实际情况调整
  function drawImg() {
    var canvasWidth = c.width
    var imageWidth = img.width
    var imageHeight = img.height
    // 计算缩放比例
    scale = canvasWidth / imageWidth
    // 计算图片缩放后的尺寸--不乘系数--原图大小
    var scaledWidth = imageWidth * scale
    var scaledHeight = imageHeight * scale
    c.height = scaledHeight
    // 绘制图片
    ctx.drawImage(img, 0, 0, scaledWidth, scaledHeight)
  }

  // 监听点击绘制
  canvasBox.addEventListener("mousedown", function (e) {
    // if(e.timeStamp<1000)
    if (e.button !== 0) return // 区分左右键
    const frame = document.createElement("div")
    frame.classList.add("frame")
    frame.innerHTML = `<div class="frame_start"></div>
    <div class="frame_end"></div><input class="frameName" placeholder="请输入名称"/>
    <img src="images/deleteIcon.png" alt="" class="delete-icon">
                <div class="frame_drag"></div>
            `
    canvasBox.appendChild(frame)
    let offset = getMouseOffset(e, this)
    frame.style.left = offset.x + "px"
    frame.style.top = offset.y + "px"
    frame.style.pointerEvents = "none" // 先禁用事件避免干扰绘制
    start = offset
    editTarget = frame
    canvasBox.addEventListener("mousemove", resizeFrame)
    frames.push(frame)
  })
  // 监听拖动
  canvasBox.addEventListener("mouseup", dragMouseUp)
  window.addEventListener("mouseup", dragMouseUp)
  function dragMouseUp(e) {
    canvasBox.removeEventListener("mousemove", resizeFrame)
    if (editTarget) {
      addFrameEventListener(editTarget)
      editTarget.dataObject = {
        start: { x: editTarget.offsetLeft, y: editTarget.offsetTop },
        end: {
          x: editTarget.offsetLeft + editTarget.offsetWidth,
          y: editTarget.offsetTop + editTarget.offsetHeight
        },
        width: editTarget.offsetWidth,
        height: editTarget.offsetHeight,
        name: ""
      }
      //   if(editTarget.offsetWidth<6)return
      datas.push(editTarget.dataObject)
    }
    editTarget = null
  }
  // 修改frame宽高与位置
  function resizeFrame(e) {
    if (editTarget) {
      let endOffset = getMouseOffset(e, canvasBox)
      let width = endOffset.x - start.x
      let height = endOffset.y - start.y
      editTarget.style.width = Math.abs(width) + "px"
      if (width < 0) {
        editTarget.style.left = start.x + width + "px"
      }
      editTarget.style.height = Math.abs(height) + "px"
      if (height < 0) {
        editTarget.style.top = start.y + height + "px"
      }
    }
  }

  // 显示数据json对象
  // checkData.onclick = function () {
  //   dataView.innerHTML = JSON.stringify(datas)
  // }
  // 监听删除
  window.addEventListener("keydown", handleKeyDown)
})
function handleKeyDown(e) {
  if (e.key == "Delete") {
    if (frames.length) {
      let len = frames.length
      frames[len - 1].remove()
      frames.splice(len - 1, 1)
      datas.splice(len - 1, 1)
    }
  }
}
// 清除数据
function clearData() {
  for (let i of frames) {
    i.remove()
  }
  frames = []
  datas = []
}

  //加载回显数据 一定保证图片加载完成之后执行loaddata
function loadData(dataArray) {
  dataArray.forEach((data) => {
    const frame = document.createElement("div")
    frame.classList.add("frame")
    frame.innerHTML = `<div class="frame_start"></div><div class="frame_end"></div><input class="frameName" placeholder="请输入扫描框名称"/> <img src="images/deleteIcon.png" alt="" class="delete-icon"> <div class="frame_drag"></div>`
    canvasBox.appendChild(frame)
    scaleData(data, scale)
    frame.style.left = data.start.x + "px"
    frame.style.top = data.start.y + "px"
    data.width = data.end.x - data.start.x
    frame.style.width = data.width + "px"
    data.height = data.end.y - data.start.y
    frame.style.height = data.height + "px"
    frame.dataObject = data
    frame.querySelector("input").value = data.name
    datas.push(data)
    frames.push(frame)
    addFrameEventListener(frame)
  })
}

function getMouseOffset(e, trigger) {
  let rect = trigger.getBoundingClientRect()
  let x = e.clientX - rect.left
  let y = e.clientY - rect.top
  return {
    x: x,
    y: y
  }
}

// 按照图片缩放比例对数据值进行处理
function scaleData(data, s) {
  data.start.x = data.start.x * s
  data.start.y = data.start.y * s
  data.end.x = data.end.x * s
  data.end.y = data.end.y * s
}
// 为frame对象以及其子元素添加事件监听
function addFrameEventListener(frame) {
  let moveStart = null
  let offset = null
  frame.style.pointerEvents = "auto"
  frame.querySelector(".frame_drag").addEventListener("mousedown", function (e) {
    moveStart = getMouseOffset(e, canvasBox)
    offset = { x: frame.offsetLeft, y: frame.offsetTop }
    window.addEventListener("mousemove", moveFrame)
    e.stopPropagation()
  })

  frame.querySelector(".frame_drag").addEventListener("mouseup", removeDragEvent)
  window.addEventListener("mouseup", removeDragEvent)

  frame.querySelector(".frame_start").addEventListener("mousedown", function (e) {
    moveStart = getMouseOffset(e, canvasBox)
    window.addEventListener("mousemove", resizeFrameStart)
    e.stopPropagation()
  })
  frame.querySelector(".frame_start").addEventListener("mouseup", function (e) {
    window.removeEventListener("mousemove", resizeFrameStart)
    updateFrame()
    e.stopPropagation()
  })
  frame.querySelector(".frame_end").addEventListener("mousedown", function (e) {
    moveStart = getMouseOffset(e, canvasBox)
    window.addEventListener("mousemove", resizeFrameEnd)
    e.stopPropagation()
  })
  frame.querySelector(".frame_end").addEventListener("mouseup", function (e) {
    window.removeEventListener("mousemove", resizeFrameEnd)
    updateFrame()
    e.stopPropagation()
  })
  frame.querySelector("input").addEventListener("mousedown", function (e) {
    e.stopPropagation()
  })
  frame.querySelector(".delete-icon").addEventListener("mousedown", function (e) {
    const index = datas.indexOf(frame.dataObject)
    frame.remove()
    datas.splice(index, 1)
    e.stopPropagation()
  })

  function removeDragEvent() {
    window.removeEventListener("mousemove", moveFrame)
  }

  function moveFrame(e) {
    let moffset = getMouseOffset(e, canvasBox)
    let left = offset.x + (moffset.x - moveStart.x)
    left = left < 0 ? 0 : left
    left = left + frame.offsetWidth > mycanvas.width ? mycanvas.width - frame.offsetWidth : left
    let top = offset.y + (moffset.y - moveStart.y)
    top = top < 0 ? 0 : top
    top = top + frame.offsetHeight > mycanvas.height ? mycanvas.height - frame.offsetHeight : top
    frame.style.left = left + "px"
    frame.style.top = top + "px"
    frame.dataObject.start.x = left
    frame.dataObject.start.y = top
    frame.dataObject.end.x = left + frame.dataObject.width
    frame.dataObject.end.y = top + frame.dataObject.height
  }
  // 拖动左上角
  function resizeFrameStart(e) {
    let moffset = getMouseOffset(e, canvasBox)
    let newOffsetX = frame.dataObject.start.x + (moffset.x - moveStart.x)
    let newOffsetY = frame.dataObject.start.y + (moffset.y - moveStart.y)
    newOffsetX = newOffsetX <= 0 ? 0 : newOffsetX
    newOffsetX = frame.dataObject.end.x - newOffsetX < 20 ? frame.dataObject.end.x - 20 : newOffsetX
    newOffsetY = newOffsetY <= 0 ? 0 : newOffsetY
    newOffsetY = frame.dataObject.end.y - newOffsetY < 20 ? frame.dataObject.end.y - 20 : newOffsetY
    frame.style.left = newOffsetX + "px"
    frame.style.top = newOffsetY + "px"

    let newWidth = frame.dataObject.width - (moffset.x - moveStart.x)
    let newHeight = frame.dataObject.height - (moffset.y - moveStart.y)
    newWidth = newWidth >= 20 ? newWidth : 20
    newHeight = newHeight >= 20 ? newHeight : 20
    frame.style.width = newWidth + "px"
    frame.style.height = newHeight + "px"
    frame.newSize = {
      width: newWidth >= 20 ? newWidth : 20,
      height: newHeight >= 20 ? newHeight : 20,
      left: newOffsetX,
      top: newOffsetY
    }
  }
  // 拖动右下角
  function resizeFrameEnd(e) {
    let moffset = getMouseOffset(e, canvasBox)
    let newWidth = frame.dataObject.width + (moffset.x - moveStart.x)
    let newHeight = frame.dataObject.height + (moffset.y - moveStart.y)
    newWidth = newWidth >= 20 ? newWidth : 20
    newWidth = newWidth + frame.offsetLeft > mycanvas.width ? mycanvas.width - frame.offsetLeft : newWidth
    newHeight = newHeight >= 20 ? newHeight : 20
    newHeight = newHeight + frame.offsetTop > mycanvas.height ? mycanvas.height - frame.offsetTop : newHeight
    frame.style.width = newWidth + "px"
    frame.style.height = newHeight + "px"
    frame.newSize = {
      width: newWidth >= 20 ? newWidth : 20,
      height: newHeight >= 20 ? newHeight : 20,
      left: frame.dataObject.start.x,
      top: frame.dataObject.start.y
    }
  }
  // 更新数据对象
  function updateFrame() {
    frame.dataObject.width = frame.newSize.width
    frame.dataObject.height = frame.newSize.height
    frame.dataObject.start.x = frame.newSize.left
    frame.dataObject.start.y = frame.newSize.top
    frame.dataObject.end.x = frame.newSize.left + frame.newSize.width
    frame.dataObject.end.y = frame.newSize.top + frame.newSize.height
  }

  frame.querySelector(".frameName").addEventListener("input", function (e) {
    frame.dataObject.name = e.target.value
  })
}

//最后获取坐标数据的方法
function getResultDatas() {
  let res_datas = JSON.parse(JSON.stringify(datas))
  res_datas.forEach((data) => {
    scaleData(data, 1 / scale)
  })
  return res_datas
}

defineExpose({
  datas,
  getResultDatas
})
</script>


<style >
.frame {
  border: 1px solid #f00;
  position: absolute;
  width: 0px;
  height: 0px;
  cursor: pointer;
}

.frame_start {
  border-radius: 10px;
  width: 10px;
  height: 10px;
  position: absolute;
  left: -5px;
  z-index: 100;
  top: -5px;
  background: #f00;
}

.frame_end {
  border-radius: 10px;
  width: 10px;
  height: 10px;
  position: absolute;
  right: -5px;
  bottom: -5px;
  z-index: 100;
  background: #f00;
}

.frameName {
  position: absolute;
  top: -26px;
  left: 15px;
  border: none;
  border-bottom: 1px solid #ccc;
  background: #ccc;
  outline: none;
  /* background: transparent; */
  color: red;
  font-weight: bold;
  width: 100px;
  font-size: 12px;
  z-index: 100;
  border: 1px solid #ccc;
}

.frame_drag {
  width: 12px;
  height: 12px;
  position: absolute;
  right: -5px;
  top: -5px;
  border-radius: 50%;
  z-index: 100;
  background: #080;
}
.delete-icon {
  width: 30px;
  height: auto;
  z-index: 100;
  position: absolute;
  top: -35px;
  left: -15px;
}
</style>

ps同事写的收藏一下 

相关推荐

  1. canvas绘制边框放大缩小拖动

    2024-04-15 04:58:01       17 阅读
  2. Qt无边框窗口拖动

    2024-04-15 04:58:01       19 阅读
  3. qt,滚动条,放大缩小拖动图片

    2024-04-15 04:58:01       42 阅读
  4. android用QT实现绘制曲线及双指放大缩小

    2024-04-15 04:58:01       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-15 04:58:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-15 04:58:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-15 04:58:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-15 04:58:01       20 阅读

热门阅读

  1. 匿名内部类

    2024-04-15 04:58:01       14 阅读
  2. C++ 泛型编程 模板

    2024-04-15 04:58:01       19 阅读
  3. rust实现双向队列

    2024-04-15 04:58:01       49 阅读
  4. es6对于Promise 对象的详解(2024-04-11)

    2024-04-15 04:58:01       22 阅读
  5. Leetcode215_数组中的第K个最大元素

    2024-04-15 04:58:01       19 阅读
  6. 算法3:寻找数组的中心下标

    2024-04-15 04:58:01       38 阅读
  7. kubernetes常用命令整理

    2024-04-15 04:58:01       17 阅读