效果预览:
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 = truethis.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" />
// 默认隐藏canvascanvasIsShow: false,// 子组件背景图片imgUrl: "",// 子组件多边形区域aiDataRegionalInfoList: [],// 子组件canvas宽度canvasWidth: 0,// 子组件canvas高度canvasHeight: 0,timeStamp: 0,// 记录新增->add 修改->edit 查看->detailtype: "",// 接收到绘制后多边形数据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 = []},