用JavaScript实现了一个简单的图像坐标点标注工具

news2024/12/26 21:42:49

这段代码实现了一个简单的图像标注工具,允许用户在加载的图像上进行点选标注,并且通过右键确认一个点序列来形成一个多边形。

标注效果如下

实现代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Image Annotation</title>
    <style>
        canvas {
            border: 1px solid black; /* 给画布添加边框 */
        }
    </style>
</head>
<body>

<!-- 文件输入控件 -->
<input type="file" id="file-input">
<!-- 用于显示图像和标注的画布 -->
<canvas id="image-canvas"></canvas>

<script>
    const fileInput = document.getElementById('file-input'); // 获取文件输入元素
    const canvas = document.getElementById('image-canvas'); // 获取画布元素
    const ctx = canvas.getContext('2d'); // 获取画布的2D渲染上下文

    let drawing = false; // 标记是否正在绘图
    let ix = -1, iy = -1; // 当前鼠标位置的坐标
    let points = []; // 当前正在绘制的点列表
    let pointList = []; // 已完成的点列表

    // 当选择文件时触发
    fileInput.addEventListener('change', function (event) {
        const file = event.target.files[0]; // 获取选择的文件
        const reader = new FileReader(); // 创建文件读取器

        // 文件读取完成后执行
        reader.onload = function (event) {
            const img = new Image(); // 创建图像对象
            img.src = event.target.result; // 将读取的数据赋值给图像源

            // 图像加载完成后执行
            img.onload = function () {
                canvas.width = img.width; // 设置画布宽度为图像宽度
                canvas.height = img.height; // 设置画布高度为图像高度
                ctx.drawImage(img, 0, 0); // 在画布上绘制图像
            };
        };
        reader.readAsDataURL(file); // 开始读取文件
    });

    // 右键菜单处理
    canvas.addEventListener('contextmenu', (event) => {
        event.preventDefault(); // 阻止默认的右键菜单

        // 创建自定义菜单
        const menu = document.createElement('div');
        menu.style.position = 'absolute';
        menu.style.left = event.clientX + 'px';
        menu.style.top = event.clientY + 'px';
        menu.style.backgroundColor = 'white';
        menu.style.border = '1px solid black';

        // 清空画布菜单项
        const clearItem = document.createElement('div');
        clearItem.textContent = '清空画布';
        clearItem.style.padding = '5px';
        clearItem.addEventListener('click', () => {
            //ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除画布
            // 恢复绘图状态
            ctx.restore();
            menu.remove(); // 移除菜单
        });
        //menu.appendChild(clearItem);

        // 复制坐标菜单项
        const copyItem = document.createElement('div');
        copyItem.textContent = '复制坐标';
        copyItem.style.padding = '5px';
        copyItem.addEventListener('click', () => {
            if (pointList.length > 0) {
                navigator.clipboard.writeText("(" + pointList[pointList.length - 1].join('),(') + ")");
                alert('坐标已复制到剪贴板');
            }
            menu.remove(); // 移除菜单
        });
        //menu.appendChild(copyItem);
        document.body.appendChild(menu); // 将菜单添加到页面
        return false;
    });

    // 鼠标按下事件
    canvas.addEventListener('mousedown', function (event) {
        if (event.button === 0) { // 左键点击
            drawing = true;
            [ix, iy] = [event.offsetX, event.offsetY]; // 获取鼠标位置
            points.push([ix, iy]); // 添加到点列表
            drawCircle(ix, iy); // 绘制点
            drawText(ix, iy); // 绘制点坐标文本
        } else if (event.button === 2) { // 右键点击
            if (points.length > 1) {
                ctx.beginPath();
                ctx.moveTo(points[points.length - 1][0], points[points.length - 1][1]);
                ctx.lineTo(points[0][0], points[0][1]);
                ctx.strokeStyle = 'green';
                ctx.lineWidth = 2;
                ctx.stroke();
                pointList.push([...points]);
                console.log(points);
                //navigator.clipboard.writeText("(" + points.join('),(') + ")");
                points = [];
            }
            ctx.drawImage(canvas, 0, 0);
        }
    });

    // 鼠标抬起事件
    canvas.addEventListener('mouseup', function (event) {
        if (event.button === 0) {
            drawing = false;
            if (points.length > 1) {
                ctx.beginPath();
                ctx.moveTo(points[points.length - 2][0], points[points.length - 2][1]);
                ctx.lineTo(points[points.length - 1][0], points[points.length - 1][1]);
                ctx.strokeStyle = 'green';
                ctx.lineWidth = 2;
                ctx.stroke();
            }
            ctx.drawImage(canvas, 0, 0);
        }
    });

    // 鼠标移出画布事件
    canvas.addEventListener('mouseout', function () {
        drawing = false;
    });

    // 绘制点函数
    function drawCircle(x, y) {
        ctx.beginPath();
        ctx.arc(x, y, 3, 0, Math.PI * 2);
        ctx.fillStyle = 'blue';
        ctx.fill();
    }

    // 绘制点坐标文本函数
    function drawText(x, y) {
        ctx.font = '16px Arial';
        ctx.fillStyle = 'red';
        let text = "(" + x + "," + y + ")";
        ctx.fillText(text, x, y);
    }

</script>

</body>
</html>

代码中还包含了创建右键菜单的功能,但相关的菜单项(清空画布和复制坐标)被注释掉了。如果想启用这些功能,只需取消注释相应的代码行即可。

自己动手实现业务代码,这只是代码片段,具体实现还需要根据业务需要做相应的更改;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1857240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

景区智慧公厕系统作用:公厕管理轻松了,游客体验提高了

在快节奏的现代生活中&#xff0c;旅游已成为人们放松心情、探索世界的重要方式。而对于景区来说&#xff0c;提供优质的公共服务设施&#xff0c;尤其是公厕&#xff0c;是提升游客满意度和整体旅游体验的关键。然而&#xff0c;传统公厕管理方式往往难以满足日益增长的游客需…

Tomcat简介与安装

目录 一、Tomcat 简介 1、Tomcat好帮手---JDK 2、安装Tomcat & JDK 1、系统环境说明 2 、安装JDK 3、安装Tomcat 二、Tomcat目录介绍 1、tomcat主目录介绍 2、webapps目录介绍 3、Tomcat配置介绍&#xff08;conf&#xff09; 4、Tomcat的管理 5、tomcat 配置管…

【全资料】信息化建设全套资料获取(原件+实际项目参考)

软件开发从需求调研到项目验收需要一系列文档的支持&#xff0c;这些文档在项目的各个阶段发挥着重要的作用。本文将详细介绍这些文档及其作用。 一、需求调研 在软件开发的前期&#xff0c;进行需求调研是非常重要的。需求调研的主要目的是了解用户需求&#xff0c;包括功能需…

博图随机生成俄罗斯方块程序

一、程序结构 1.定义基础数据&#xff0c;俄罗斯方块图形共19中&#xff0c;使用WORD编码存储在数组内 2.添加随机生成int数值的FC函数块&#xff0c;生成1-19 的随机数 3.查找数组内图形显示在HMI画面上 二、程序 1.生成1-19 的随机数&#xff0c;并显示当前图形样式 2.生成按…

数智化浪潮下的零售品牌商品计划革新

在数字化和智能化交织的时代背景下&#xff0c;零售品牌的商品计划正在经历一场前所未有的革新。这场革新不仅改变了商品计划的方式和流程&#xff0c;更重塑了零售品牌的竞争格局和市场地位。 一、数智化&#xff1a;零售品牌的新引擎 在快速变化的市场环境中&#xff0c;零…

【经验分享】免费版虚拟机VMware Workstation Pro 17下载方式

【经验分享】免费版虚拟机VMware Workstation Pro 17下载方式 前言一、免费虚拟机下载方式二、 安装过程总结 前言 我真的是服了&#xff0c;现在的CSDN时效性为什么这么差了。都快一个月了还没有博主更新个人免费版虚拟机VMware Workstation Pro&#xff0c;甚至很多人还不知…

Excel 解析十六进制并查找

A1 格由多个人名及其考勤情况组成&#xff0c;比如&#xff0c;c 是十六进制的 1100&#xff0c;表示第 1、2 天到场&#xff0c;第 3、4 天缺席。目前只有 4 天的考勤。 AB1alice,c,bob,7,clara,a,mike,9/input: name and presence22/input: the day to be queried 要求根据…

【计算机体系结构】

第一章 计算机体系结构的基本概念 知识点 1.冯诺依曼描述的计算机的四个部分 2.指令驱动 3.存储程序计算机在体系结构上的主要特点 4.计算机体系结构的三个方面 5.计算机组成 6.计算机系统的多层次结构 7.翻译和解释 8.一种指令集结构可以有多种组成&#xff0c;同样一种组成可…

建筑监理工程师考试试题及答案,分享几个实用搜题和学习工具 #微信#知识分享

这些软件以其强大的搜索引擎和智能化的算法&#xff0c;为广大大学生提供了便捷、高效的解题方式。下面&#xff0c;让我们一起来了解几款备受大学生欢迎的搜题软件吧&#xff01; 1.彩虹搜题 这是个老公众号了 一款考试搜题神器&#xff0c;包含执业医师、财务会计、建筑消…

Ubuntu22.04 搭建 PCL 环境(VTK源码安装),PCL测试代码

Ubuntu 22.04LTS&#xff1b;cmake-3.25.0&#xff1b;VTK-8.2&#xff1b;PCL-1.12 1. 安装 VTK 1) 安装 ccmake 和 vtk 依赖项&#xff1a; sudo apt-get install freeglut3-dev sudo apt-get install cmake cmake-gui freeglut3库说明&#xff1a; freeglut3-dev 是一个用…

什么是多态?一文彻底搞懂!

什么是多态 面向对象程序设计有三要素&#xff1a;封装、继承&#xff08;或组合&#xff09;、多态&#xff0c;前两者较好理解&#xff0c;多态总让人困惑&#xff0c;不知道具体有什么作用&#xff0c;更不知道为什么要用多态。今天就来详细分析下什么是多态&#xff0c;以…

运筹系列93:VRP精确算法

1. MTZ模型 MTZ是Miller-Tucker-Zemlin inequalities的缩写。除了定义是否用到边 x i j x_{ij} xij​外&#xff0c;还需要定义一个 u i u_i ui​用来表示此时车辆的当前载货量。注意这里x变量需要定义为有向。 这里定义为pickup问题&#xff0c;代码为&#xff1a; using Ju…

【CentOS7】Linux安装Docker教程(保姆篇)

文章目录 查看是否已安装卸载&#xff08;已安装过&#xff09;docker安装友情提示 更多相关内容可查看 注&#xff1a;本篇为Centos7安装Docker&#xff0c;若为其他系统请理性参考 查看是否已安装 如果已安装&#xff0c;请卸载重新安装 docker --version这里显示已安装 …

Web网页端IM产品RainbowChat-Web的v7.0版已发布

一、关于RainbowChat-Web RainbowChat-Web是一套Web网页端IM系统&#xff0c;是RainbowChat的姊妹系统&#xff08;RainbowChat是一套基于开源IM聊天框架 MobileIMSDK (Github地址) 的产品级移动端IM系统&#xff09;。 ► 详细介绍&#xff1a;http://www.52im.net/thread-2…

基于Java医院药品交易系统详细设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

AI大模型战争:通用与垂直,谁将领跑未来?

文章目录 &#x1f4d1;引言一、通用大模型&#xff1a;广泛适用&#xff0c;实力不容小觑1.1 强大的泛化能力1.2 广泛的适用场景 二、垂直大模型&#xff1a;专注深度&#xff0c;精准解决问题2.1 深度专注&#xff0c;精准度高2.2 快速落地与普及 三、通用与垂直&#xff1a;…

SFP4006-ASEMI无人机专用SFP4006

编辑&#xff1a;ll SFP4006-ASEMI无人机专用SFP4006 型号&#xff1a;SFP4006 品牌&#xff1a;ASEMI 封装&#xff1a;TO-247 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;40A 最大循环峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;600V 最大正向…

SVN学习(004 subversive操作和解决冲突)

尚硅谷SVN高级教程(svn操作详解) 总时长 4:53:00 共72P 此文章包含第42p-第p43的内容 操作 新建一个teacher类 添加到版本库&#xff08;也可以忽略这步 直接提交&#xff09; 资源-》右键-》team-》提交 另一个用户进行更新 资源-》右键-》team-》更新 解决冲突 用…