vue2 Canvas 多边形区域绘制组件封装

效果预览:

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 = []
    },

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

web网页精选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值