当前位置: 首页 > article >正文

Vue3 图片标框功能实现方案

基于 Vue3 组合式 API 的图片标框画框、标注、选框完整实现核心逻辑封装在 GetBoxes 组件里复制就能用一、功能说明✅ 在图片上鼠标拖拽画矩形框✅ 实时显示框坐标x, y, width, height✅ 支持多个框同时显示✅ 支持清空所有框✅ 框可渲染在图片上方不破坏原图✅ 框可拖动移动位置✅ 框可拖拽右下角调整大小✅ 单个删除框✅ 每个框自定义输入标注文字✅ 每个框自动随机不同颜色✅ 回显已保存的标框直接传数组即可✅ 保留原有多框、拖拽画框、坐标实时输出二、使用方式2.1把代码保存为 GetBoxes.vueGetBoxes.vuetemplatedivclassbox-containerdivclassimage-wrapperrefwrapperRefmousedownstartDrawmousemovehandleMouseMovemouseupstopDrawmouseleavestopDrawimg refimgRef:srcimgUrlalt标框底图loadinitCanvas/canvas refcanvasRefclassdraw-canvas/canvasdiv v-for(box, index) in boxes:keyindexclassbox-label:style{left:${box.x}px,top:${box.y-28}px,color:box.color,}input v-modelbox.labeltypetextplaceholder输入标注mousedown.stop/buttonclick.stopdeleteBox(index)×/button/div/divdivclasstool-barbuttonclickclearAllBoxes清空所有框/buttonbuttonclickconsoleLogBoxes打印所有框数据/buttondivclassbox-listh4已标框{{boxes.length}}/h4div v-for(box, index) in boxes:keyindexclassbox-item:style{ borderLeftColor: box.color }框{{index1}}{{box.label||未命名}}br/x:{{box.x}},y:{{box.y}},w:{{box.width}},h:{{box.height}}/div/div/div/div/templatescript setupimport{ref,onMounted,nextTick,onUnmounted}fromvueconstimgUrlref(https://picsum.photos/900/600)constwrapperRefref(null)constcanvasRefref(null)constimgRefref(null)letctxnullconstboxesref([])constcurrentBoxref(null)constisDrawingref(false)constisDraggingref(false)constisResizingref(false)constdragStartref({x:0,y:0})constactiveIndexref(-1)// 随机边框颜色constrandomColor(){constcolors[#FF4757,#FF6B35,#F79F1F,#A3CB38,#00D2D3,#3742fa,#FDA7DF,#ED4C67,#1B9CFC,#F8EFBA,#58B19F,#D6A2E8,]returncolors[Math.floor(Math.random()*colors.length)]}// 初始化画布constinitCanvasasync(){awaitnextTick()constccanvasRef.valueconstimgimgRef.value c.widthimg.offsetWidth c.heightimg.offsetHeight ctxc.getContext(2d)redrawCanvas()}// 鼠标移动 切换指针样式consthandleMouseMove(e){if(!ctx)returnconstrectcanvasRef.value.getBoundingClientRect()constmxe.clientX-rect.leftconstmye.clientY-rect.top canvasRef.value.style.cursorcrosshairfor(letiboxes.value.length-1;i0;i--){constbboxes.value[i]constrightb.xb.widthconstbottomb.yb.height// 缩放控制点if(mxright-12mxrightmybottom-12mybottom){canvasRef.value.style.cursorse-resizebreak}// 边框区域if((mxb.x-2mxb.x2myb.ymybottom)||(mxright-2mxright2myb.ymybottom)||(myb.y-2myb.y2mxb.xmxright)||(mybottom-2mybottom2mxb.xmxright)){canvasRef.value.style.cursormovebreak}// 框内部if(mxb.xmxrightmyb.ymybottom){canvasRef.value.style.cursorpointerbreak}}drawing(e)}// 开始绘制、拖动、缩放conststartDraw(e){constrectcanvasRef.value.getBoundingClientRect()constmxe.clientX-rect.leftconstmye.clientY-rect.topfor(letiboxes.value.length-1;i0;i--){constbboxes.value[i]constrightb.xb.widthconstbottomb.yb.heightif(mxright-12mxrightmybottom-12mybottom){isResizing.valuetrueactiveIndex.valueireturn}if(mxb.xmxrightmyb.ymybottom){isDragging.valuetrueactiveIndex.valuei dragStart.value{x:mx-b.x,y:my-b.y}return}}isDrawing.valuetruecurrentBox.value{x:mx,y:my,width:0,height:0,color:randomColor(),label:}}// 绘制拖拽逻辑constdrawing(e){if(!ctx)returnconstrectcanvasRef.value.getBoundingClientRect()constmxe.clientX-rect.leftconstmye.clientY-rect.topif(isDrawing.valuecurrentBox.value){currentBox.value.widthmx-currentBox.value.x currentBox.value.heightmy-currentBox.value.y}if(isDragging.valueactiveIndex.value-1){constboxboxes.value[activeIndex.value]box.xmx-dragStart.value.x box.ymy-dragStart.value.y}if(isResizing.valueactiveIndex.value-1){constboxboxes.value[activeIndex.value]box.widthmx-box.x box.heightmy-box.y}redrawCanvas()}// 结束操作conststopDraw(){if(isDrawing.valuecurrentBox.value){const{width,height}currentBox.valueif(Math.abs(width)8Math.abs(height)8){boxes.value.push({...currentBox.value})}}isDrawing.valuefalseisDragging.valuefalseisResizing.valuefalsecurrentBox.valuenullactiveIndex.value-1redrawCanvas()}// 重绘画布constredrawCanvas(){if(!ctx)returnctx.clearRect(0,0,canvasRef.value.width,canvasRef.value.height)boxes.value.forEach((box){ctx.strokeStylebox.color ctx.lineWidth2ctx.strokeRect(box.x,box.y,box.width,box.height)ctx.fillStylebox.colorconstrxbox.xbox.widthconstrybox.ybox.height ctx.fillRect(rx-6,ry-6,12,12)})if(currentBox.value){ctx.strokeStylecurrentBox.value.color ctx.strokeRect(currentBox.value.x,currentBox.value.y,currentBox.value.width,currentBox.value.height)}}// 删除单个框constdeleteBox(index){boxes.value.splice(index,1)redrawCanvas()}// 清空全部constclearAllBoxes(){boxes.value[]redrawCanvas()}// 打印框数据constconsoleLogBoxes(){console.log(所有框数据,JSON.parse(JSON.stringify(boxes.value)))}// 回显历史标注示例constloadSavedBoxes(){constsavedData[{x:50,y:50,width:120,height:100,color:#FF4757,label:人物},{x:200,y:150,width:180,height:140,color:#3742fa,label:车辆},]boxes.valuesavedDataredrawCanvas()}//四舍五入函数保留小数点后n位constcustomRound(number,decimals){constfactorMath.pow(10,decimals);returnMath.round(number*factor)/factor;};onMounted((){if(imgRef.value.complete)initCanvas()// loadSavedBoxes()})onUnmounted((){ctxnull})/scriptstyle scoped.box-container{width:100%;max-width:900px;margin:20px auto;}.image-wrapper{position:relative;width:fit-content;}.draw-canvas{position:absolute;top:0;left:0;z-index:10;}img{display:block;max-width:100%;}.box-label{position:absolute;z-index:20;display:flex;gap:6px;align-items:center;}.box-label input{width:100px;padding:2px 6px;font-size:12px;border:1px solid #ddd;border-radius:4px;}.box-label button{background:#ff4757;color:white;border:none;width:18px;height:18px;font-size:12px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;}.tool-bar{margin-top:12px;display:flex;gap:10px;align-items:center;}button{padding:6px 12px;background:#3742fa;color:#fff;border:none;border-radius:4px;cursor:pointer;}.box-list{margin-top:10px;}.box-item{padding:6px 10px;margin:4px0;font-size:13px;border-left:4px solid #ddd;background:#f9f9f9;}/style2.2在你的页面中直接引入使用templatedivh3图片标框工具/h3GetBoxes//div/templatescript setupimportGetBoxesfrom./GetBoxes.vue/script三、总结Vue3 标准组合式 API写法基于 Canvas 实现标框性能好、不操作 DOM代码可直接运行坐标实时输出支持多框、清空只需要替换图片地址、接入接口就能用于项目

相关文章:

Vue3 图片标框功能实现方案

基于 Vue3 组合式 API 的图片标框(画框、标注、选框)完整实现,核心逻辑封装在 GetBoxes 组件里,复制就能用 一、功能说明 ✅ 在图片上鼠标拖拽画矩形框 ✅ 实时显示框坐标(x, y, width, height) ✅ 支持多…...

Java数组工具类实战:设计不可实例化的静态工具类

实现一个工具类 MathUtils,满足以下要求: 1. 所有方法均为静态,且该类不能从外部实例化(提示:使用私有构造器)。 2. 提供三个静态方法:- maxArray(int[] arr):返回较大值&#xff1b…...

极致精简,功能强大的PDF编辑工具

这是一款功能全面的PDF编辑工具 你只需要导入一份PDF格式文件 就可以快速的对它进行插入 批注编辑保护转换等各种操作 而且无需登录 也可以直接使用 在插入选项中可以进行插入文字图片 页面页眉页脚页码文档背景水印视频音频等 在批注选项中可以管理批注隐藏批注 高亮显示 文本…...

Agent开发面试通关攻略:吃透稳拿offer

阅读前置:2026年当下最卷也最缺人的AI岗位,一定是AI Agent开发。最近刷遍CSDN、牛客、力扣最新面经,发现一个非常明显的招聘趋势:普通大模型微调岗位饱和内卷,而AI Agent开发岗位人才严重缺口,薪资更高、竞…...

告别手写UI!用NXP GUI Guider拖拽设计LVGL界面,5分钟搞定音乐播放器Demo

嵌入式UI开发革命:5分钟用GUI Guider构建LVGL音乐播放器在嵌入式系统开发中,用户界面(UI)设计曾长期是工程师的痛点——既要考虑资源受限的硬件环境,又要实现流畅美观的交互体验。传统手动编写UI代码的方式不仅效率低下,调试过程更…...

告别外部中断!用EnableInterrupt库轻松搞定Arduino Nano多通道PWM读取(附完整代码)

Arduino Nano多通道PWM读取实战:用EnableInterrupt突破硬件限制当你用Arduino Nano开发四轴飞行器或机器人项目时,是否遇到过这样的尴尬:遥控器的四个通道PWM信号需要同时读取,但Nano只有两个外部中断引脚?这个问题困扰…...

力扣HOT100(30)两两交换链表中的节点

链表的交换要注意 “链表不断链”。前驱和后继都要连着迭代法(必学死磕!O (n) 时间,O (1) 空间)1. 为什么必须用虚拟头节点?因为交换后链表的头节点会变! 比如示例 1 中,原来的头是 1&#xff0…...

Wechat2RSS:微信公众号转RSS订阅工具

文章目录Wechat2RSS:微信公众号转RSS订阅工具Wechat2RSS:微信公众号转RSS订阅工具 ttttmr开源的Wechat2RSS项目,目前在GitHub上获得1409颗Star,项目地址为https://github.com/ttttmr/Wechat2RSS。该工具的核心作用是将微信公众号…...

微信小程序3D开发框架技术对比:XR-Frame与threejs-miniprogram

随着微信小程序逐步支持3D渲染与AR能力,开发者面临两个主要官方方案:自研的XR-Frame和适配Three.js的threejs-miniprogram。本文将从架构设计、渲染机制、功能集成、开发模式及适用场景等维度进行技术分析,为技术选型提供参考。一、XR-Frame&…...

为什么视频代剪辑会影响你的内容传播效果

为什么你精心拍的视频,发出去却没人看? 你有没有过这样的经历:花了一整天拍Vlog,素材画质高清、内容真实,可一剪出来就显得平淡无奇,点赞寥寥?或者婚礼当天感动全场,回看成片却像流水…...

ARMv8 HFGITR_EL2寄存器解析与虚拟化指令陷阱控制

1. AArch64 HFGITR_EL2寄存器架构解析HFGITR_EL2(Hypervisor Fine-Grained Instruction Trap Register)是ARMv8架构中专门用于指令级陷阱控制的系统寄存器,属于虚拟化扩展的重要组成部分。这个64位寄存器通过位映射机制实现对特定AArch64指令…...

ThinkPad开机报错0183/0253?别慌,手把手教你搞定EFI变量错误(附BIOS重置教程)

ThinkPad开机报错0183/0253?EFI变量错误全面解决方案当你按下ThinkPad的电源键,期待熟悉的开机画面时,屏幕上却突然跳出一串神秘代码——"0183: Bad CRC of Security Settings in EFI Variable"或"0253: EFI Variable Block D…...

告别FTP龟速:用NTFS-3G在CentOS7上直连移动硬盘拷贝200G大文件

告别FTP龟速:用NTFS-3G在CentOS7上直连移动硬盘拷贝200G大文件当面对数百GB的设计素材、日志文件或数据库备份需要迁移时,传统的FTP传输往往会成为效率瓶颈。我曾在一个视频处理项目中,需要将230GB的4K原始素材从移动硬盘导入服务器&#xff…...

别再盲跑了!手把手教你用Arduino Zero在IDE 2.0里设置断点单步调试

告别盲跑时代:Arduino Zero与IDE 2.0的源码级调试实战指南 当你的Arduino项目逻辑越来越复杂,仅靠串口打印调试就像在迷宫里摸黑前行——直到遇见Arduino Zero与IDE 2.0的调试组合。本文将揭示如何用这套工具实现 源码级精准调试 ,即使你手…...

浏览器 Profile 环境排查:Cookie、LocalStorage、网络出口与自动化任务配置清单

一、为什么浏览器环境经常“今天能用,明天失效”很多团队遇到登录状态丢失、页面配置异常、自动化任务失败时,会先怀疑网络、脚本或系统本身。但在实际项目里,问题经常不是单点故障,而是浏览器环境缺少稳定管理:对象常…...

飞书远程控机:OpenClaw配置全攻略

本文详细介绍如何通过 OpenClaw 工具对接飞书开放平台,配置智能机器人实现 Windows 电脑的远程控制。主要内容涵盖文件管理和程序启动等核心功能的实现方法,并提供完整的配置指南与常见问题解决方案。 一、使用前提说明 1. 系统要求 仅适用于 Windows…...

ARM架构CONSTRAINED UNPREDICTABLE行为解析与应对

1. ARM架构中的CONSTRAINED UNPREDICTABLE行为解析在处理器架构设计中,UNPREDICTABLE行为通常指架构规范未明确定义的执行结果,可能导致不可预期的系统状态。ARM架构通过引入CONSTRAINED UNPREDICTABLE机制,将这类行为限制在特定范围内&#…...

亚马逊 Rufus 关停,Alexa 正式上线:卖家必须读懂的6条新规则

2026年5月13日,亚马逊官方正式宣布,下线Rufus,推出全新AI购物助手:Alexa for Shopping。但是,这不是粗暴地直接下线 Rufus,而是一次购物AI底层架构的重组 —— 将 Rufus 的商品专长 与 Alexa的用户理解力&a…...

OpenClaw 连接阿里云百炼图文教程

OpenClaw 连接阿里云百炼图文教程 前置准备 已安装并可以正常打开 OpenClaw Windows。 OpenClaw 顶部 Gateway 状态保持在线。 已准备好可正常登录的阿里云账号。 可以正常访问阿里云百炼登录地址:https://bailian.console.aliyun.com/cn-beijing#/home 建议提…...

政企数据安全:危机与出路

随着数字化转型的浪潮席卷全球,公共部门积累的数据量呈爆炸式增长。从公民个人信息到公共服务记录,从财政预算到基础设施管理数据——这些宝贵资源在提升政府治理效率的同时,也悄然成为网络犯罪分子的“新猎物”。当公共数据逐渐成为数字时代…...

2026 西安 AI 问答曝光搭建技术解析:GEO 知识图谱 + 深度测评

随着大语言模型技术的快速普及,AI 搜索已经成为用户获取企业信息、商家服务的核心入口。根据中国互联网信息中心 2026 年发布的《中国人工智能搜索发展报告》显示,2025 年国内 AI 搜索用户规模突破 8.2 亿,日均搜索请求超过 20 亿次&#xff…...

SSE 基础知识

SSE 基础知识 一、概念定义 SSE 全称 Server-Sent Events,是基于HTTP协议的服务器单向数据推送技术。 建立一次长连接后,服务端可主动持续向前端推送数据,无需客户端反复轮询请求。 二、核心特点 单向通信:仅服务器 → 客户端发送…...

BurpSuite 2025插件开发JDK版本兼容性实战指南

1. 为什么BurpSuite插件开发环境总在JDK版本上翻车?你是不是也经历过:下载好BurpSuite最新版2025.4,兴冲冲打开插件开发文档,照着官方示例写完第一个HelloWorld插件,一编译——java.lang.UnsupportedClassVersionError…...

sudo企业级应用【20260525】001篇

文章目录 一、总体设计思路 1️⃣ 设计原则 2️⃣ 日志策略(重点) 二、10 个真实生产场景(含 sudoers 配置) 🔹 Linux 系统管理(3 个) ✅ 场景 1:基础运维(用户 / 权限) ✅ 场景 2:磁盘与文件系统 ✅ 场景 3:网络与防火墙 🔹 云管理(2 个) ✅ 场景 4:云 CLI …...

Redis分布式锁进阶第二十篇

一、本篇前置衔接 第二十篇我们完成了全系列终局复盘,整理了故障排查SOP与企业级落地铁律。常规单资源锁、热点分片锁、隔离锁全部讲透,但真实复杂业务永远不是单一资源:下单要扣库存、扣优惠券、扣积分、冻结余额,多资源并行争抢…...

串口通信粘包问题:成因深度解析与项目实战解决方案

在嵌入式开发、工业工控、上位机下位机交互项目中,串口(RS232/RS485)是最基础、最常用的通信方式。绝大多数开发者都遇到过这样的问题:串口接收的数据偶尔错乱、解析报错、数据拼接异常,单次接收的数据时而半包、时而多…...

【UniApp小程序开发】解决无法使用Vue自定义指令的完美替代方案:权限组件封装

在 UniApp 开发中,你是否遇到过这样的困惑:明明在 Vue Web 项目中用得顺手的 v-permission 自定义指令,一到小程序端就完全失效?本文将深入剖析其原因,并提供一套可直接复用的组件化解决方案,让你在小程序中…...

SkillVLA:通过技能复用应对双-臂操纵中的组合多样性

26年3月来自新加坡国立、北京中关村学院、上海创新研究院、上海AI实验室、上海交大和复旦的论文“SkillVLA: Tackling Combinatorial Diversity in Dual-Arm Manipulation via Skill Reuse”。 视觉-语言-动作(VLA)模型近期取得的进展,已充分…...

2026 新视角:化妆品开发的底层逻辑,做好一款产品,从选对原料开始

在化妆品研发链条中,配方架构、生产工艺、包装设计固然重要,但决定一款产品上限的,永远是原料。一款稳定、安全、表现优异的护肤成品,离不开纯净、达标、批次一致的优质原料。对于品牌方、配方师、代工企业而言,原料不…...

Windows 10/11系统下,SecureCRT 8.7.2保姆级安装与激活图文指南(含Keygen使用避坑点)

Windows平台SecureCRT 8.7.2全流程部署与安全配置指南在当今远程运维与网络管理的日常工作中,一款可靠的终端仿真工具如同工程师的瑞士军刀。作为行业标杆的SecureCRT,其8.7.2版本在Windows 10/11环境下的部署却常让新手陷入各种技术陷阱——从安装路径选…...