首页 > 其他分享 >vue移动鼠标画矩形(抄别人的,下附原文地址)

vue移动鼠标画矩形(抄别人的,下附原文地址)

时间:2023-10-16 14:24:20浏览次数:41  
标签:sY vue 鼠标 list ctx cav value && 矩形

1、draw.js

/**
 * 画布中绘制矩形
 * 参数: cav-画布对象  list-矩形数组 i-选中矩形下标
 **/
/* 操作执行方法分发 */
export function draw(cav, list, i) {
    // 画布初始化
    let ctx = cav.getContext('2d');
    ctx.strokeStyle = 'blue';
    ctx.lineWidth = 2;
 
    // 变量初始化
    let sX = 0; // 鼠标X坐标
    let sY = 0; // 鼠标Y坐标
 
    /*
     *鼠标移动进行第一层判断, 区分情况: 无矩形, 已有矩形无选中, 已有选中矩形
     */
    cav.onmousemove = function (em) {
        sX = em.offsetX;
        sY = em.offsetY;
        let iem = undefined; // 鼠标移动时临时存储当前鼠标所在矩形的下标
 
        if (list.length === 0) { // **** 无矩形 ****
            // 绘制新矩形
            newDraw(cav, ctx, list);
        } else if (i === undefined) { // **** 已有矩形无选中 ****
            // 判断鼠标位置
            list.forEach(function (value, index, array) {
                if (value.w > 0 && value.h > 0 && sX > value.x && sX < value.x + value.w && sY > value.y && sY < value.y + value.h) {
                    // 鼠标在右下方向生成的矩形中
                    iem = index;
                    judgeDraw(cav, ctx, list, iem);
                }
                if (value.w < 0 && value.h > 0 && sX < value.x && sX > value.x + value.w && sY > value.y && sY < value.y + value.h) {
                    // 鼠标在左下方向生成的矩形中
                    iem = index;
                    judgeDraw(cav, ctx, list, iem);
                }
                if (value.w > 0 && value.h < 0 && sX > value.x && sX < value.x + value.w && sY < value.y && sY > value.y + value.h) {
                    // 鼠标在右上方向生成的矩形中
                    iem = index;
                    judgeDraw(cav, ctx, list, iem);
                }
                if (value.w < 0 && value.h < 0 && sX < value.x && sX > value.x + value.w && sY < value.y && sY > value.y + value.h) {
                    // 鼠标在左上方向生成的矩形中
                    iem = index;
                    judgeDraw(cav, ctx, list, iem);
                }
                if (iem === undefined) {
                    // 鼠标不在矩形中
                    newDraw(cav, ctx, list);
                }
            })
        } else { // **** 已有选中矩形 ****
            // 判断鼠标位置
            for (let index = 0; index < list.length; index++) {
                let value = list[index];
                if (sX < value.x + 5 && sX > value.x - 5 && sY < value.y + 5 && sY > value.y - 5) {
                    // ***  鼠标在起点角  ***
                    if (index === i) {
                        changeDraw(cav, ctx, list, i, 1);
                        break;
                    }
                } else if (sX < value.x + value.w + 5 && sX > value.x + value.w - 5 && sY < value.y + 5 && sY > value.y - 5) {
                    // ***  鼠标在起点横向角  ***
                    if (index === i) {
                        changeDraw(cav, ctx, list, i, 2);
                        break;
                    }
 
                } else if (sX < value.x + 5 && sX > value.x - 5 && sY < value.y + value.h + 5 && sY > value.y + value.h - 5) {
                    // ***  鼠标在起点纵向角  ***
                    if (index === i) {
                        changeDraw(cav, ctx, list, i, 3);
                        break;
                    }
 
                } else if (sX < value.x + value.w + 5 && sX > value.x + value.w - 5 && sY < value.y + value.h + 5 && sY > value.y + value.h - 5) {
                    // ***  鼠标在终点角  ***
                    if (index === i) {
                        changeDraw(cav, ctx, list, i, 4);
                        break;
                    }
 
                } else if (value.w > 0 && value.h > 0 && sX > value.x && sX < value.x + value.w && sY > value.y && sY < value.y + value.h) {
                    // ***  鼠标在右下方向生成的矩形中  ***
                    iem = index
                    judgeDraw(cav, ctx, list, index);
                    break;
 
                } else if (value.w < 0 && value.h > 0 && sX < value.x && sX > value.x + value.w && sY > value.y && sY < value.y + value.h) {
                    // ***  鼠标在左下方向生成的矩形中  ***
                    iem = index
                    judgeDraw(cav, ctx, list, index);
                    break;
 
                } else if (value.w > 0 && value.h < 0 && sX > value.x && sX < value.x + value.w && sY < value.y && sY > value.y + value.h) {
                    // ***  鼠标在右上方向生成的矩形中  ***
                    iem = index
                    judgeDraw(cav, ctx, list, index);
                    break;
 
                } else if (value.w < 0 && value.h < 0 && sX < value.x && sX > value.x + value.w && sY < value.y && sY > value.y + value.h) {
                    // ***  鼠标在左上方向生成的矩形中  ***
                    iem = index
                    judgeDraw(cav, ctx, list, index);
                    break;
 
                } else {
                    if (iem === undefined) {
                        // *** 鼠标不在矩形中 ***
                        newDraw(cav, ctx, list);
                    }
                }
            }
        }
 
        /* 鼠标移出画布区域时保存选中矩形下标(如有) */
        cav.onmouseout = function (eo) {
            if (i !== undefined) {
                // 初始化
                draw(cav, list, i);
            }
        };
    }
};
 
/* 编辑矩形四个角 */
function changeDraw(cav, ctx, list, i, site) {
    cav.style.cursor = 'pointer'
 
    // site: 操作矩形角的位置, 1-起点 2-起点横向 3-起点纵向 4-终点
    let mark = list[i];
 
    /* 按下鼠标左键 */
    cav.onmousedown = function (ed) {
        // 保存鼠标落下位置的X, Y坐标, firefox中鼠标移动后ed.offsetX ed.offsetY会变成 0, 需要使用临时参数存储起来
        let sX = ed.offsetX; // 起点X坐标
        let sY = ed.offsetY; // 起点Y坐标
 
        /* 移动鼠标 */
        cav.onmousemove = function (em) {
            // 计算绘制数据
            let iframe = {}
            switch (site) {
                case 1:
                    iframe = {
                        x: em.offsetX,
                        y: em.offsetY,
                        w: mark.w - (em.offsetX - sX),
                        h: mark.h - (em.offsetY - sY)
                    }
                    break;
                case 2:
                    iframe = {
                        x: mark.x,
                        y: mark.y + (em.offsetY - sY),
                        w: mark.w + (em.offsetX - sX),
                        h: mark.h - (em.offsetY - sY)
                    }
                    break;
                case 3:
                    iframe = {
                        x: mark.x + (em.offsetX - sX),
                        y: mark.y,
                        w: mark.w - (em.offsetX - sX),
                        h: mark.h + (em.offsetY - sY)
                    }
                    break;
                case 4:
                    iframe = {
                        x: mark.x,
                        y: mark.y,
                        w: mark.w + (em.offsetX - sX),
                        h: mark.h + (em.offsetY - sY)
                    }
                    break;
            }
            list.splice(i, 1, iframe);
 
            // 重新绘制
            reDraw(cav, ctx, list, i);
        }
 
        /* 鼠标离开矩形区 */
        cav.onmouseout = function (eo) {
            // 重新绘制
            reDraw(cav, ctx, list);
            // 初始化
            draw(cav, list)
        };
 
        /* 监听键盘, 点击后可以控制删除, 由于移动矩形事件已经监听了onmousemove, 所以在移动矩形方法中仍有一次调用 */
        delDraw(cav, ctx, list, i);
    }
 
};
 
/* 绘制新矩形 */
function newDraw(cav, ctx, list) {
    cav.style.cursor = 'crosshair'
    // 初始化变量
    let start = false; // 画框状态, false时不执行画框操作
    let sX = 0; // 起点X坐标
    let sY = 0; // 起点Y坐标
 
    /* 按下鼠标左键 */
    cav.onmousedown = function (ed) {
        /* 使用变量 */
        start = true;
        sX = ed.offsetX;
        sY = ed.offsetY;
 
        /* 重置按键监听, 防止选中取消后仍可删除 */
        delDraw(cav, ctx, list, null)
 
        /* 鼠标移动 */
        cav.onmousemove = function (em) {
            if (start) {
                // 重新绘制
                reDraw(cav, ctx, list);
                // 设置边框为虚线
                ctx.beginPath();
                ctx.setLineDash([8, 4]);
                ctx.rect(sX, sY, em.offsetX - sX, em.offsetY - sY);
                ctx.stroke();
            }
        }
 
        /* 鼠标抬起 */
        cav.onmouseup = function (eu) {
            if (start && Math.abs(eu.offsetX - sX) > 10 && Math.abs(eu.offsetY - sY) > 10) {
                // 改变矩形数组
                let frame = {
                    x: sX, y: sY, w: eu.offsetX - sX, h: eu.offsetY - sY
                };
                list.push(frame);
                // 重新绘制
                reDraw(cav, ctx, list);
                // 改变画框状态
                start = false
                // 初始化
                draw(cav, list)
            } else {
                // 重新绘制
                reDraw(cav, ctx, list);
                // 改变画框状态
                start = false
                // 初始化
                draw(cav, list)
            }
        };
 
        /* 鼠标离开矩形区 */
        cav.onmouseout = function (eo) {
            if (start && Math.abs(eo.offsetX - sX) > 10 && Math.abs(eo.offsetY - sY) > 10) {
                // 改变矩形数组
                let frame = {
                    x: sX, y: sY, w: eo.offsetX - sX, h: eo.offsetY - sY
                };
                list.push(frame);
                // 重新绘制
                reDraw(cav, ctx, list);
                // 改变画框状态
                start = false;
                // 初始化
                draw(cav, list)
            } else {
                // 重新绘制
                reDraw(cav, ctx, list);
                // 改变画框状态
                start = false
                // 初始化
                draw(cav, list)
            }
        };
    }
};
 
/* 选中矩形, 重绘矩形, 并分发后续事件 */
function judgeDraw(cav, ctx, list, iem) {
    cav.style.cursor = 'default'
    // 初始化变量
    let sX = 0; // 起点X坐标
    let sY = 0; // 起点Y坐标
 
    /* 按下鼠标左键 */
    cav.onmousedown = function (ed) {
        sX = ed.offsetX;
        sY = ed.offsetY;
 
        // 更改选中状态, 重绘矩形
        reDraw(cav, ctx, list, iem);
 
        /* 当仅点击选中矩形便抬起鼠标后, 重新初始化画布 */
        cav.onmouseup = function () {
            // 重绘矩形
            reDraw(cav, ctx, list, iem);
 
            // 初始化
            draw(cav, list, iem);
        };
 
        /* 按住拖动鼠标, 移动选中矩形*/
        moveDraw(cav, ctx, list, iem, sX, sY);
 
        /* 监听键盘, 点击后可以控制删除, 由于移动矩形事件已经监听了onmousemove, 所以在移动矩形方法中仍有一次调用 */
        delDraw(cav, ctx, list, iem);
    }
};
 
/* 移动矩形 */
function moveDraw(cav, ctx, list, i, sX, sY) {
    let mark = list[i]
    cav.onmousemove = function (em) {
        let iframe = {
            x: mark.x + (em.offsetX - sX),
            y: mark.y + (em.offsetY - sY),
            w: mark.w,
            h: mark.h
        }
        list.splice(i, 1, iframe);
        /* 监听键盘, 使矩形在移动后仍可删除, 在点击未移动过的矩形时仍有一次监听 */
        delDraw(cav, ctx, list, i);
        // 重新绘制
        reDraw(cav, ctx, list, i);
    }
 
    cav.onmouseup = function () {
        // 重绘矩形
        reDraw(cav, ctx, list, i);
 
        // 初始化
        draw(cav, list, i);
    };
};
 
/* 删除矩形 */
function delDraw(cav, ctx, list, i) {
    /* 按键事件 */
    if (i === null) {
        // i为null时阻止按键监听事件冒泡
        cav.onkeydown = function (k) {
            return false;
        }
    } else {
        // 监听按键事件
        cav.onkeydown = function (k) {
            let key = k.keyCode || k.which;
            if (key == 8 && i !== null) {
                if (list.length >= 1) {
                    // 删除数组元素
                    list.splice(i, 1);
                    // 重绘矩形
                    reDraw(cav, ctx, list);
                } else {
                    /* 矩形数组长度为0, 已将矩形框全部删除 */
                    ctx.clearRect(0, 0, cav.width, cav.height);
                }
                // 重置监听状态, 防止删除完毕后, 按键监听不消失
                delDraw(cav, ctx, list, null)
                // 重绘矩形
                reDraw(cav, ctx, list);
                // 初始化
                draw(cav, list);
            }
        }
    }
};
 
/* 重绘所有矩形 */
function reDraw(cav, ctx, list, i) {
    ctx.setLineDash([8, 0]); // 设置边框为实线
    ctx.clearRect(0, 0, cav.width, cav.height);
    // 绘制未选中部分
    list.forEach(function (value, index, array) {
        if (i === undefined || index != i) {
            ctx.beginPath();
            ctx.strokeStyle = 'blue';
            ctx.rect(value.x, value.y, value.w, value.h);
            ctx.stroke();
        }
    });
    // 绘制已选中部分
    list.forEach(function (value, index, array) {
        if (index === i) {
            /* 绘制方框 */
            ctx.beginPath();
            ctx.strokeStyle = 'red';
            ctx.rect(value.x, value.y, value.w, value.h);
            ctx.fillStyle = 'RGBA(102,102,102,0.2)'
            ctx.fillRect(value.x, value.y, value.w, value.h);
            ctx.stroke();
            // 绘制四个角的圆圈
            ctx.beginPath();
            ctx.strokeStyle = 'red';
            ctx.arc(value.x, value.y, 4, 0, Math.PI * 2)
            ctx.fillStyle = "red";
            ctx.fill();// 画起点实心圆
            ctx.stroke();
            ctx.beginPath();
            ctx.arc(value.x, value.y + value.h, 4, 0, Math.PI * 2);
            ctx.fillStyle = "red";
            ctx.fill();// 画起点纵向实心圆
            ctx.stroke();
            ctx.beginPath();
            ctx.arc(value.x + value.w, value.y + value.h, 4, 0, Math.PI * 2);
            ctx.fillStyle = "red";
            ctx.fill();// 画起点横向实心圆
            ctx.stroke();
            ctx.beginPath();
            ctx.arc(value.x + value.w, value.y, 4, 0, Math.PI * 2);
            ctx.fillStyle = "red";
            ctx.fill();// 画终点实心圆
            ctx.stroke();
        }
    })
};

2、.vue单文件

<template>
    <div class="content">
        <img src="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg" />
        <canvas ref="markCanvas" tabindex='0'></canvas>
    </div>
</template>
<script>
import {draw} from "@/utils/draw"; // 矩形绘制方法
export default {
    name: 'marks',
    data() {
        return {
            markList: [], // 标记内容数组
        }
    },
    mounted() {
        this.initCanvas(); // 画布初始化
    },
    methods: {
        /* 画布初始化 */
        initCanvas() {
            let that = this
            this.$nextTick(() => {
                // 初始化canvas宽高
                let cav = this.$refs.markCanvas;
                cav.width = '800';
                cav.height = '600';
                let ctx = cav.getContext('2d');
                ctx.strokeStyle = 'blue'
                cav.style.cursor = 'crosshair'
                
                // 计算使用变量
                let list = this.markList; // 画框数据集合, 用于服务端返回的数据显示和绘制的矩形保存
                // 若服务端保存的为百分比则此处需计算实际座标, 直接使用实际座标可省略
                list.forEach(function (value, index, array) {
                    let newValue = {
                        x: value.x * cav.width,
                        y: value.y * cav.height,
                        w: value.w * cav.width,
                        h: value.h * cav.height,
                    }
                    list.splice(index, 1, newValue)
                })
                
                // 若list长度不为0, 则显示已标记框
                if (list.length !== 0) {
                    list.forEach(function (value, index, array) {
                        // 遍历绘制所有标记框
                        ctx.rect(value.x, value.y, value.w, value.h);
                        ctx.stroke();
                    });
                }
                
                // 调用封装的绘制方法
                draw(cav,list);
 
                // 备注: js中对象操作指向的是对象的物理地址, 获取绘制完矩形的结果数组直接取用或处理this.markList即可
            })
        },
    },
</script>
<style lang='scss' scoped>
.content {
    position: relative;
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateX(-50%);
    width: 800px;
    height: 600px;
    
    img {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 9;
    }
 
    canvas {
        position: absolute;
        top: 0;
        left: 0;
        z-index: 10;
    }
}
</style>

效果:

 原文:https://blog.csdn.net/Sunshine_YXJ/article/details/108216971

标签:sY,vue,鼠标,list,ctx,cav,value,&&,矩形
From: https://www.cnblogs.com/lyt520/p/17767236.html

相关文章

  • Vue学习笔记(十一):路由管理
      1Vue路由基本使用¶1.1安装¶Vue中默认并不提供路由功能,需要安装其插件Vue-router,如下所示,其中“@3”表示安装版本[email protected]创建路由¶在src目录下创建路由文件目录,目录名为“router”,并在该目录下创建“index.js”文件,文件内容如下所示,代码......
  • [Vue]模板语法和MVVM
    模板语法分为:①插值语法、②指令语法插值语法{{xxx}}指令语法v-bind:attr='xxx' 注意v-bind:只是一种指令,指令可以有很多种。v-bind:可以简写为:<body><divid="root"><h1>插值语法</h1><h3>{{name}}</h3><hr/&g......
  • 天地图 vue引入
    官网使用引入基础引入创建......
  • vue @click.native/stop/prevent
    [email protected]父组件要引用子组件中的点击事件,可以通过@click.native来直接访问子组件中的方法,如果不使用@click.native可在子组件中使用this.$emit('click')来传递事件//父组件<template><div><span>父组件页面</span><search@click="onSubmit"></search&g......
  • (CH592-CH305)2K鼠标上报率
    前言:使用CH592与CH305可实现2K鼠标上报率功能,具体功能和实现做以下讲解。描述:需要使用2块CH592的开发板和1块CH305开发板。2块CH592分别作为2.4GMouse(TX)和Dongle(RX)端的无线设备;CH305作为Dongle端连接上位机。连接示意图参考:代码烧录与接线:①烧录:由沁恒官方提供的3份......
  • 手撕Vue-数据驱动界面改变下
    经过上一篇的介绍,已经实现了观察者模式的基本内容,接下来要完成的就是将上一篇的发布订阅模式运用到Nue中,实现数据驱动界面改变。在监听数据变化的章节当中,根据指定的区域和数据去编译渲染界面这个步骤处,我写了一个注释,这个注释是这样的:第一步:给外界传入的所有数据都添加get/se......
  • Vue源码学习(十一):计算属性computed初步学习
    好家伙,  1.Computed实现原理if(opts.computed){initComputed(vm,opts.computed);}functioninitComputed(vm,computed){//存放计算属性的watcherconstwatchers=vm._computedWatchers={};for(constkeyincomputed){constuser......
  • 开源项目 | 一款基于NodeJs+Vue3的强大的在线设计图片工具
     一、项目概述一款漂亮且功能强大的在线海报图片设计器,仿稿定设计。适用于海报图片生成、电商分享图、文章长图、视频/公众号封面等多种场景。二、技术特性丝滑的操作体验,丰富的交互细节,基础功能完善采用服务端生成图片,确保多端出图统一性,支持各种CSS特性简易AI......
  • vue3中setup
    和vue2不同的是vue3中的script中带有setup标签里面的setup相当于vue2中的data和methds空间可以放置函数,也可以执行函数在写时需要有return返回值<scriptsetup></script>setup执行发生在页面之前所以不能使用this函数,一般通过ref绑定组件上的值进行修改 使用函数例子......
  • 手撕Vue-数据驱动界面改变上
    经过上一篇的介绍,已经实现了监听数据的变化,接下来就是要实现数据变化后,界面也跟着变化,这就是数据驱动界面改变。想要实现数据变化之后更新UI界面,我们可以使用发布订阅模式来实现,先定义一个观察者类,再定义一个发布订阅类,然后再通过发布订阅的类来管理观察者类。接下来我们就......