<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同事写的收藏一下