效果预览:
CanvasBox组件
<!-- 区域设置canvas -->
<template>
<div class="all" ref="divideBox">
<!-- <div>
<button @click="test">清空</button>
</div> -->
<img id="img" v-if="imgUrl" :src="imgUrl">
<!-- width="700" height="450" -->
<canvas id="mycanvas" ref="mycanvas" :width="canvasWidth" :height="canvasHeight" @mousedown="canvasDown($event)"
@mousemove="canvasMove($event)" @mouseup="canvasUp($event)" @dblclick="doubleclick()">浏览器不支持canvas</canvas>
</div>
</template>
<script>
export default {
props: {
// canvas宽度
canvasWidth: {
type: Number,
default: 0
},
// canvas高度
canvasHeight: {
type: Number,
default: 0
},
// 时间戳
timeStamp: {
type: Number,
default: 0
},
// 图片
imgUrl: {
type: String,
default: ""
},
// 是否可编辑 detail 不可编辑
type: {
type: String,
default: ""
},
// 多边形区域
aiDataRegionalInfoList: {
type: Array,
default: []
},
// 摄像头id
// cameraId: {
// default: null
// }
},
watch: {
// 清空画布
timeStamp() {
this.test();
},
// 接收到多边形区域数据
aiDataRegionalInfoList(val) {
// console.log("接收到多边形区域数据", val);
let newArray = []
if (val && val.length > 0) {
val.forEach(item => {
newArray.push({
"cor_x": item.x,
"cor_y": item.y
})
})
}
if (newArray && newArray.length === 0) return;
this.drawPolygon(newArray)
// this.draw(val);
// this.drawPolygon([
// {
// "cor_x": 443,
// "cor_y": 223
// },
// {
// "cor_x": 672,
// "cor_y": 197
// },
// {
// "cor_x": 562,
// "cor_y": 368
// }
// ])
}
},
data() {
return {
isMultiple: false, // 是否支持多个多边形绘制
observer: null,
x: null,
y: null,
isdraw: false, //是否在画图形
ctx: null, //canvas对象
coordinates: [], //一个多边形的坐标信息
cor_index: 0, //当前多边形的索引
endtip: false, //是否结束一个多边形的绘制
all_coordinates: [], //所有多边形的信息
isdrag: false, //是否正在拖动
drag_index: [-1, -1], //当前拖动的多边形坐标
}
},
mounted() {
// 监听画布尺寸变化
// this.initObserver();
setTimeout(() => {
//初始化画布对象
this.initDraw()
}, 500)
},
// beforeDestroy() {
// // 销毁监听器
// this.cleanupObserver();
// },
methods: {
// 初始化监听器
// initObserver() {
// this.observer = new MutationObserver(mutations => {
// mutations.forEach(mutation => {
// if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
// this.handleResize();
// }
// });
// });
// this.observer.observe(this.$refs.divideBox, { attributes: true });
// this.handleResize(); // 初始化时也调用一次
// },
// 销毁监听器
// cleanupObserver() {
// if (this.observer) {
// this.observer.disconnect();
// }
// },
// 监听到尺寸变化
// handleResize() {
// const divideBox = this.$refs.divideBox;
// const canvas = this.$refs.myCanvas;
// if (divideBox && canvas) {
// const newWidth = divideBox.clientWidth;
// const newHeight = divideBox.clientHeight;
// // console.log('Divide box width:', newWidth);
// // console.log('Divide box height:', newHeight);
// // 更新 canvas 的尺寸
// canvas.width = newWidth;
// canvas.height = newHeight;
// // 进行其他初始化操作
// } else {
// console.error('未找到分割框或画布元素');
// }
// },
// 监听到尺寸变化
// updateBoxWidth(width) {
// let widthVal = width
// let heightVal = width / 1920 * 1080
// // console.log("width", widthVal)
// // console.log("height", heightVal)
// let pointList = JSON.parse(JSON.stringify(this.oldPointList))
// pointList.forEach(item => {
// item.x = item.x / 1920 * widthVal
// item.y = item.y / 1080 * heightVal
// item.type = item.type
// item.isShow = item.isShow
// })
// this.pointList = pointList
// // console.log("更新位置", this.pointList);
// },
// updateCanvasSize() {
// const canvas = this.$refs.mycanvas;
// this.canvasWidth = canvas.clientWidth;
// this.canvasHeight = canvas.clientHeight;
// },
// 清空画布
test() {
this.all_coordinates = [];
this.coordinates = [];
this.isdraw = false;
this.endtip = false;
// this.ctx.clearRect(0, 0, 700, 450);
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
},
// 填充区域
fillarea() {
this.ctx.fillStyle = 'rgba(255, 255, 0,0.4)';
for (let i = 0; i < this.all_coordinates.length; i++) {
let cors = this.all_coordinates[i];
let x0 = cors[0].cor_x;
let y0 = cors[0].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(x0, y0);
for (let j = 1; j < cors.length; j++) {
let x = cors[j].cor_x;
let y = cors[j].cor_y;
this.ctx.lineTo(x, y);
}
this.ctx.fill();
this.ctx.closePath();
}
},
//初始化画布对象
initDraw() {
const canvas = document.querySelector("#mycanvas");
this.ctx = canvas.getContext("2d");
// this.ctx.strokeStyle = 'rgb(255, 255, 0)';
this.ctx.strokeStyle = 'yellow';
},
// 判断是否是拖拽点
isdragpoint(x, y) {
if (this.all_coordinates.length == 0) {
return false;
}
for (let i = 0; i < this.all_coordinates.length; i++) {
for (let j = 0; j < this.all_coordinates[i].length; j++) {
let px = this.all_coordinates[i][j].cor_x;
let py = this.all_coordinates[i][j].cor_y;
if (Math.abs(x - px) <= 5 && Math.abs(y - py) <= 5) {
this.drag_index[0] = i;
this.drag_index[1] = j;
return true;
}
}
}
return false;
},
// 画布中鼠标按下
canvasDown(e) {
if (this.type === 'detail') return;
// console.log(123, e.offsetX, e.offsetY);
// const cameraId = this.cameraId
// if (cameraId == "" || cameraId == undefined || cameraId == null) {
// this.$message({
// message: '请先选择摄像头',
// type: 'warning'
// });
// return;
// }
// console.log("鼠标按下", this.coordinates);
// console.log("鼠标按下", this.all_coordinates);
let x = e.offsetX;
let y = e.offsetY;
if (this.isdragpoint(x, y)) {
this.isdrag = true;
return 0;
}
//画布中鼠标按下
if (this.endtip) {//已经结束了上个多边形的绘制,把上个多边形的坐标放入数组,同时清空单个多边形数组信息
this.endtip = false;
}
if (this.all_coordinates && this.all_coordinates.length == 1) {
this.$message({
message: '最多只能绘制一个区域',
type: 'warning'
});
this.coordinates = []
return;
}
//获取鼠标按下的坐标,放入数组中
this.coordinates.push({ cor_x: x, cor_y: y });
this.isdraw = true; //正在画多边形
},
// 画布中鼠标移动
drawlines() {
//把所有多边形画出来
for (let i = 0; i < this.all_coordinates.length; i++) {
let cors = this.all_coordinates[i];
//前后坐标连线
for (let j = 0; j < cors.length - 1; j++) {
this.ctx.beginPath();
let x0 = cors[j].cor_x;
let y0 = cors[j].cor_y;
let x1 = cors[j + 1].cor_x;
let y1 = cors[j + 1].cor_y;
this.ctx.moveTo(x0, y0);
this.ctx.lineTo(x1, y1);
this.ctx.stroke();
this.ctx.closePath();
}
//最后一个与第一个连线
let begin_x = cors[0].cor_x;
let begin_y = cors[0].cor_y;
let end_x = cors[cors.length - 1].cor_x;
let end_y = cors[cors.length - 1].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(begin_x, begin_y);
this.ctx.lineTo(end_x, end_y);
this.ctx.stroke();
this.ctx.closePath();
}
},
//把当前绘制的多边形之前的坐标线段绘制出来
drawline() {
for (let i = 0; i < this.coordinates.length - 1; i++) {
this.ctx.beginPath();
let x0 = this.coordinates[i].cor_x;
let y0 = this.coordinates[i].cor_y;
let x1 = this.coordinates[i + 1].cor_x;
let y1 = this.coordinates[i + 1].cor_y;
this.ctx.moveTo(x0, y0);
this.ctx.lineTo(x1, y1);
this.ctx.stroke();
this.ctx.closePath();
}
},
// 针对的是单个多边形的所有顶点
drawcircle() {
//为当前的多边形端点画圆
this.ctx.fillStyle = 'rgb(255, 255, 0)';
for (let i = 0; i < this.coordinates.length; i++) {
let x = this.coordinates[i].cor_x;
let y = this.coordinates[i].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(x, y);
this.ctx.arc(x, y, 5, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
}
},
// 用来在画布上标示出每个多边形顶点的位置,并且为这些顶点画出小圆圈
drawcircles() {
//为所有的多边形端点画圆
this.ctx.fillStyle = 'rgb(255, 255, 0)';
for (let i = 0; i < this.all_coordinates.length; i++) {
let cors = this.all_coordinates[i];
for (let j = 0; j < cors.length; j++) {
let x = cors[j].cor_x;
let y = cors[j].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(x, y);
this.ctx.arc(x, y, 5, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
}
}
},
// 画布中鼠标抬起
canvasUp(e) {
if (this.isdrag) {
this.isdrag = false;
// 打印所有多边形的顶点坐标
this.all_coordinates.forEach((polygon, index) => {
// console.log(`多边形 ${index + 1} 的顶点坐标:`);
polygon.forEach((vertex, vertexIndex) => {
// console.log(` 顶点 ${vertexIndex + 1}: (${vertex.cor_x}, ${vertex.cor_y})`);
});
});
// console.log("拖拽点后抬起", this.all_coordinates);
this.$emit("all_coordinates", this.all_coordinates[0]);
}
this.drag_index = [-1, -1];
// this.drag_index = [e.layerX, e.layerY];
// console.log("鼠标抬起", e);
},
// 画布中点击后线条鼠标移动中
canvasMove(e) {
//画布中鼠标移动
//没开始画或者结束画之后不进行操作
let x = e.offsetX;
let y = e.offsetY;
if (this.isdrag) {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
// this.ctx.clearRect(0, 0, 700, 450);
this.all_coordinates[this.drag_index[0]][this.drag_index[1]].cor_x = x;
this.all_coordinates[this.drag_index[0]][this.drag_index[1]].cor_y = y;
this.drawlines();
this.drawcircles();
this.fillarea();
}
if (this.coordinates.length == 0 || !this.isdraw || this.endtip) {
return 0;
}
//获取上一个点
let last_x = this.coordinates[this.coordinates.length - 1].cor_x;
let last_y = this.coordinates[this.coordinates.length - 1].cor_y;
// this.ctx.clearRect(0, 0, 700, 450); //清空画布
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.drawline();//把之前的点连线
this.drawcircle();
// 如果不止一个多边形,把多边形们画出来
if (this.all_coordinates.length != 0) {
this.drawlines();
this.drawcircles();
this.fillarea();
}
//获取鼠标移动时的点,画线,实现线段跟踪效果。
this.ctx.beginPath();
this.ctx.moveTo(last_x, last_y);
this.ctx.lineTo(x, y);
this.ctx.strokeStyle = 'yellow';
this.ctx.stroke();
this.ctx.closePath();
},
// 双击画布结束连线
doubleclick() {
if (this.type === 'detail') return;
//双击画布,在最后一个点的时候双击,自动连线第一个点,同时宣告画结束
let x0 = this.coordinates[0].cor_x;
let y0 = this.coordinates[0].cor_y;
let x1 = this.coordinates[this.coordinates.length - 1].cor_x;
let y1 = this.coordinates[this.coordinates.length - 1].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(x0, y0);
this.ctx.lineTo(x1, y1);
this.ctx.stroke();
this.ctx.closePath();
this.isdraw = false;
this.endtip = true
this.drawcircle();
this.coordinates.pop();
this.all_coordinates.push(this.coordinates);
this.ctx.fillStyle = 'rgba(255, 255, 0, 0.4)';
let bx = this.coordinates[0].cor_x;
let by = this.coordinates[0].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(bx, by);
for (let k = 1; k < this.coordinates.length; k++) {
let x = this.coordinates[k].cor_x;
let y = this.coordinates[k].cor_y;
this.ctx.lineTo(x, y)
}
this.ctx.fill();
this.ctx.closePath();
// console.log("绘制完毕获得数据", this.coordinates)
// console.log("绘制完毕获得全部数据", this.all_coordinates)
this.$emit("all_coordinates", this.all_coordinates[0]);
// console.log(666, this.all_coordinates[0]);
this.coordinates = [];
},
// 接收多边形数据并绘制
// receivePolygonData(polygonData) {
// if (Array.isArray(polygonData)) {
// // 清空画布
// this.test();
// // 绘制每个多边形
// polygonData.forEach(cors => {
// console.log(111, cors);
// this.drawPolygon(cors);
// });
// }
// },
// 在画布中绘制多边形并添加可拖拽的顶点
drawPolygon(coordinates) {
// 清空画布
this.test();
if (coordinates.length >= 2) {
this.ctx.beginPath();
let x0 = coordinates[0].cor_x;
let y0 = coordinates[0].cor_y;
this.ctx.moveTo(x0, y0);
// 绘制多边形的边
for (let j = 1; j < coordinates.length; j++) {
let x = coordinates[j].cor_x;
let y = coordinates[j].cor_y;
this.ctx.lineTo(x, y);
}
// 最后一个点回到第一个点闭合多边形
this.ctx.lineTo(x0, y0);
this.ctx.closePath();
this.ctx.stroke();
// 填充多边形
this.ctx.fillStyle = 'rgba(255, 255, 0, 0.4)';
this.ctx.fill();
// 绘制多边形顶点的小圆圈
if (this.type != "detail") {
this.ctx.fillStyle = 'rgb(255, 255, 0)';
for (let j = 0; j < coordinates.length; j++) {
let x = coordinates[j].cor_x;
let y = coordinates[j].cor_y;
this.ctx.beginPath();
this.ctx.arc(x, y, 5, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
}
// 打印所有顶点的坐标
// console.log('绘制完成的多边形顶点坐标:', coordinates);
this.$emit("all_coordinates", coordinates);
}
let newArray = []
newArray.push(coordinates)
this.all_coordinates = newArray
// console.log(666, this.all_coordinates, newArray);
}
}
},
};
</script>
<style lang="scss" scoped>
.all {
position: relative;
width: 100%;
height: 100%;
// border: 2px solid red;
border: 1px solid yellow;
}
#mycanvas {
// border: 1px solid red;
// width: 100%;
// height: 100%;
// width: 700px;
// height: 45px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
// border: 2px solid red;
}
#img {
width: 100%;
height: 100%;
user-select: none;
}
</style>
<CanvasBox v-if="canvasIsShow" :imgUrl="imgUrl" :aiDataRegionalInfoList="aiDataRegionalInfoList"
:canvasWidth="canvasWidth" :canvasHeight="canvasHeight" :timeStamp="timeStamp"
@all_coordinates="getCoordinates" :type="type" />
// 默认隐藏canvas
canvasIsShow: false,
// 子组件背景图片
imgUrl: "",
// 子组件多边形区域
aiDataRegionalInfoList: [],
// 子组件canvas宽度
canvasWidth: 0,
// 子组件canvas高度
canvasHeight: 0,
timeStamp: 0,
// 记录新增->add 修改->edit 查看->detail
type: "",
// 接收到绘制后多边形数据
getCoordinates(val) {
// console.log("接收到canvas区域数据", val);
let newArray = []
if (val && val.length > 0) {
val.forEach(item => {
newArray.push({
x: this.autoPageSizeToDefault(item.cor_x, item.cor_y).x,
y: this.autoPageSizeToDefault(item.cor_x, item.cor_y).y,
})
})
}
this.form.aiDataRegionalInfoList = newArray
},
// 清空画布
clearCanvas() {
this.timeStamp = Date.now();
this.aiDataRegionalInfoList = []
this.form.aiDataRegionalInfoList = []
},