js使用canvas实现画roi功能,并实现交集并集差集操作,附源码

效果概览

支持圆形,矩形,旋转矩形绘制,鼠标像素拾取,图片缩放,图片拖拽,像素测量,roi交集并集补集输出
TODO:实现自由路径绘制,与后台交互数据
gif

实现原理

交集并集差集使用像素做运算,使用0代表没有像素,1代表有像素,然后再做运算

    // 计算交集
    calculateIntersection(shape1, shape2) {
        return shape1.map((pixel, index) => pixel && shape2[index] ? 1 : 0);
    }

    // 计算并集
    calculateUnion(shape1, shape2) {
        return shape1.map((pixel, index) => pixel || shape2[index] ? 1 : 0);
    }

    // 计算差集
    calculateDifference(shape1, shape2) {
        return shape1.map((pixel, index) => pixel && !shape2[index] ? 1 : 0);
    }

canvas事件实现

使用两个canvas,使用隐藏的OffscreenCanvas来判断鼠标命中的是哪个元素,需要把shape同时画到两个canvas中,获取隐藏canvas中的像素值就知道命中哪一个

事件分发器

class EventSimulator {
    constructor() {
      // 初始化事件监听器映射对象
      this.listenersMap = {};
      // 初始化最后的鼠标按下和移动的 ID
      this.lastDownId = null;
      this.lastMoveId = null;
    }
  
    // 添加事件动作
    addAction(action, evt) {
      const { type, id } = action;
  
      // 如果是鼠标移动事件
      if (type === ActionType.Move) {
        // 触发 mousemove 事件
        this.fire(id, EventNames.mousemove, evt);
        
        // 如果存在最后的移动 ID 且与当前 ID 不同
        if (this.lastMoveId && this.lastMoveId !== id) {
          // 触发 mouseleave 事件
          this.fire(this.lastMoveId, EventNames.mouseleave, evt);
          // 触发 mouseenter 事件
          this.fire(id, EventNames.mouseenter, evt);
        }
      }
  
      // 如果是鼠标按下事件
      if (type === ActionType.Down) {
        // 触发 mousedown 事件
        this.fire(id, EventNames.mousedown, evt);
      }
  
      // 如果是鼠标释放事件
      if (type === ActionType.Up) {
        // 触发 mouseup 事件
        this.fire(id, EventNames.mouseup, evt);
  
        // 如果最后的按下 ID 等于当前 ID,则触发 click 事件
        if (this.lastDownId === id) {
          this.fire(id, EventNames.click, evt);
        }
      }
  
      // 更新最后的移动和按下 ID
      if (type === ActionType.Move) {
        this.lastMoveId = action.id;
      } else if (type === ActionType.Down) {
        this.lastDownId = action.id;
      }
    }
  
    // 添加事件监听器
    addListeners(id, listeners) {
      this.listenersMap[id] = listeners;
    }
  
    // 触发事件
    fire(id, eventName, evt) {
      // 检查是否有对应 ID 和事件名称的监听器,如果有则依次执行监听器函数
      if (this.listenersMap[id] && this.listenersMap[id][eventName]) {
        this.listenersMap[id][eventName].forEach((listener) => listener(evt));
      }
    }
}

shape控制点实现

每个shape都有对应的控制点,控制点也绘制在OffscreenCanvas中,通过添加事件来控制shape

旋转矩形的控制点实现

handleCtrlPointMove(x,y,evts){
        //判断拖动的哪个点
        //顺时针方向
        if(evts.activateId==this.ctrlDotId[0]||evts.activateId==this.ctrlDotId[1]||evts.activateId==this.ctrlDotId[2]||evts.activateId==this.ctrlDotId[3]){//左上
            let p2 = getNextPoint(this.x,this.y,20,this.phi);
            let p1 = {x:this.x,y:this.y};
            let p3 = {x:x,y:y};
            let dist1=distanceToLine(p3,p1,p2);

            let phi2=radianToVerticalAngle(this.phi);
            let p22 = getNextPoint(this.x,this.y,20,phi2);
            let dist2=distanceToLine(p3,p1,p22);

            this.w=dist1*2;
            this.h=dist2*2;  
        }else if(evts.activateId==this.ctrlDotId[4]){//旋转
            var dx = x-this.x;
            var dy = y-this.y;
            this.phi=-Math.atan2(dy,dx)
        }

        evts.redraw();
        
    }

画布拖拽实现

使用ctx的setTransform实现

画布缩放实现

使用ctx的setTransform实现

this.ctx.setTransform(CanvasStatus.scale,0,0,CanvasStatus.scale,CanvasStatus.offset.x,CanvasStatus.offset.y);

shape移动实现

通过改变shape的内部属性重绘shape实现

class Circle extends BaseShape{
    constructor(opts = {}) {
        super();
        this.type=ShapeType.circle;
        Object.assign(this, {
            x: 0,
            y: 0,
            radius: 0,
            strokeWidth: 1,
            strokeColor: '#e6a23c',
            fillColor: '#fff'
        }, opts);
    }

    handleCtrlPointMove(x,y,evts){
        //最小10个像素
        let diffX=x-this.x;
        this.radius=Math.max(diffX,10);
        if(diffX>4){
            evts.redraw();
        }
    }

    drawMask(ctx){
        const x = this.x;
        const y = this.y;
        const radius = this.radius;
        let strokeColor = this.makStrokeColor;
        let fillColor = this.maskFillColor;
        const strokeWidth = this.strokeWidth;
        //ctx.save();
        ctx.beginPath();
        ctx.fillStyle = fillColor;
        ctx.strokeStyle = strokeColor;
        ctx.lineWidth = strokeWidth;
        ctx.arc(x, y, radius, 0, Math.PI * 2);
        ctx.fill();
        ctx.stroke();
        //ctx.restore();
    }

    toShape(shapesOperator){
        return shapesOperator.createShapeCircle(this.x,this.y,this.radius);
    }
    }

详细实现

TODO

源码下载

https://download.csdn.net/download/isyoungboy/89072580?spm=1001.2014.3001.5503

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【HTML】标签学习(下.4)

&#xff08;Hello&#xff01;大家好哇&#xff0c;今天我们将继续学习HTML的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; &#xff08;续接【HTML】标签学习&#xff08;下.3&#xff09;&#xff09; 3.4.2 <label&g…

Java设计模式:代理模式的静态和动态之分(八)

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 在软件设计中&#xff0c;代理模式是一种常用的设计模式&#xff0c;它为我们提供了一种方式来控制对原始对象的访问。在Java中&a…

Python快速入门系列-9(Python项目实战)

第九章:Python项目实战 9.1 开发一个简单的Web应用9.1.1 项目概述9.1.2 环境准备9.1.3 项目结构9.1.4 代码实现9.1.4.1 创建数据库模型9.1.4.2 创建视图9.1.4.3 实用工具函数9.1.4.4 运行应用9.1.5 模板设计9.2 数据分析与可视化项目9.2.1 项目概述9.2.2 环境准备9.2.3 数据分…

vulnhub之devguru靶场提权过程(vulnhub打靶日记)

一、环境搭建 VM版本&#xff1a;17.5.1 build-23298084 攻击机&#xff1a;Kali2024&#xff08;下载地址&#xff1a;https://www.kali.org/&#xff09; 靶机&#xff1a;vulnhub靶场Devguru&#xff08;下载地址&#xff1a;https://www.vulnhub.com/entry/devguru-1,62…

探索网红系统功能菜单架构的设计与优化

随着社交媒体和数字化内容的普及&#xff0c;网红经济正在成为新兴的产业。在网红经济体系中&#xff0c;网红系统的功能菜单架构对于平台的用户体验和运营效率至关重要。本文将深入探讨网红系统功能菜单架构的设计与优化&#xff0c;为网红经济的发展提供新的思路和方法。 --…

【Web】记录Polar靶场<困难>难度题一遍过

目录 上传 PHP是世界上最好的语言 非常好绕的命令执行 这又是一个上传 网站被黑 flask_pin veryphp 毒鸡汤 upload tutu Unserialize_Escape 自由的文件上传系统​​​​​​​ ezjava 苦海 你想逃也逃不掉 safe_include CB链 phar PHP_Deserializatio…

Stable Diffusion WebUI 图片信息(PNG Info)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 本文主要讲解 Stable Diffusion WebUI 的图片信息功能&#xff0c;主要包括&#xff1a;获取生成参数、将图片发送到其…

Spark实战:词频统计

文章目录 一、Spark实战&#xff1a;词频统计&#xff08;一&#xff09;Scala版1、分步完成词频统计2、一步搞定词频统计 &#xff08;二&#xff09;Python版1、分步完成词频统计2、一步搞定词频统计 二、实战总结 一、Spark实战&#xff1a;词频统计 &#xff08;一&#x…

【黑马头条】-day05延迟队列文章发布审核-Redis-zSet实现延迟队列-Feign远程调用

文章目录 昨日回顾今日内容1 延迟任务1.1 概述1.2 技术对比1.2.1 DelayQueue1.2.2 RabbitMQ1.2.3 Redis实现1.2.4 总结 2 redis实现延迟任务2.0 实现思路2.1 思考2.2 初步配置实现2.2.1 导入heima-leadnews-schedule模块2.2.2 在Nacos注册配置管理leadnews-schedule2.2.3 导入表…

STM32应用开发——使用PWM+DMA驱动WS2812

STM32应用开发——使用PWMDMA驱动WS2812 目录 STM32应用开发——使用PWMDMA驱动WS2812前言1 硬件介绍1.1 WS2812介绍1.1.1 芯片简介1.1.2 引脚描述1.1.3 工作原理1.1.4 时序1.1.5 传输协议 1.2 电路设计 2 软件编程2.1 软件原理2.2 测试代码2.2.1 底层驱动2.2.2 灯效应用 2.3 运…

css实现更改checkbox的样式;更改checkbox选中后的背景色;更改checkbox选中后的icon

<input class"check-input" type"checkbox"> .check-input {width: 16px;height: 16px;} /* 设置默认的checkbox样式 */input.check-input[type"checkbox"] {-webkit-appearance: none; /* 移除默认样式 */border: 1px solid #999;outl…

go连接数据库(原生)

根据官网文档 Go Wiki: SQL Database Drivers - The Go Programming Language 可以看到go可以连接的关系型数据库 ​ 常用的关系型数据库基本上都支持&#xff0c;下面以mysql为例 下载mysql驱动 打开上面的mysql链接 GitHub - go-sql-driver/mysql: Go MySQL Driver i…

【已解决】Error: error:0308010C:digital envelope routines::unsupported

前言 场景&#x1f3ac; 使用 Ant Design &#xff0c; 执行 npm run dev 出现异常。 文章目录 前言场景&#x1f3ac; 异常信息解决方案方案一(推荐)MAC | Linux 电脑成功⬇️ Windows 电脑 方案2&#xff1a; 不懂留言 JavaPub 异常信息 我直接异常信息&#xff0c;你可以…

Python快速入门系列-8(Python数据分析与可视化)

第八章:Python数据分析与可视化 8.1 数据处理与清洗8.1.1 数据加载与查看8.1.2 数据清洗与处理8.1.3 数据转换与整理8.2 数据可视化工具介绍8.2.1 Matplotlib8.2.2 Seaborn8.2.3 Plotly8.3 数据挖掘与机器学习简介8.3.1 Scikit-learn8.3.2 TensorFlow总结在本章中,我们将探讨…

【嵌入式智能产品开发实战】(十五)—— 政安晨:通过ARM-Linux掌握基本技能【GNU C标准与编译器】

目录 GNU C 什么是C语言标准 C语言标准的内容 C语言标准的发展过程 1.K&R C 2.ANSI C 3.C99标准 4.C11标准 编译器对C语言标准的支持 编译器对C语言标准的扩展 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 嵌入式智能产品…

QA测试开发工程师面试题满分问答6: 如何判断接口功能正常?从QA的角度设计测试用例

判断接口功能是否正常的方法之一是设计并执行相关的测试用例。下面是从测试QA的角度设计接口测试用例的一些建议&#xff0c;包括功能、边界、异常、链路、上下游和并发等方面&#xff1a; 通过综合考虑这些测试维度&#xff0c;并设计相应的测试用例&#xff0c;可以更全面地评…

【机器学习】“强化机器学习模型:Bagging与Boosting详解“

1. 引言 在当今数据驱动的世界里&#xff0c;机器学习技术已成为解决复杂问题和提升决策制定效率的关键工具。随着数据的增长和计算能力的提升&#xff0c;传统的单一模型方法已逐渐无法满足高精度和泛化能力的双重要求。集成学习&#xff0c;作为一种结合多个学习算法以获得比…

大数据实验二-HDFS编程实践

一&#xff0e;实验内容 HDFS编程实践&#xff1a; 1&#xff09;使用HDFS文件操作的常用Shell命令&#xff1b; 2&#xff09;利用Hadoop提供的Java API进行基本的文件操作。 二&#xff0e;实验目的 1、理解HDFS在Hadoop体系结构中的角色。 2、熟练使用HDFS操作常用的Sh…

【测试篇】接口测试

接口测试&#xff0c;可以用可视化工具 postman。 如何做接口测试&#xff1f;&#xff1f; 我们可以先在浏览器中随机进入一个网页&#xff0c;打开开发者工具&#xff08;F12&#xff09;。 随便找一个接口Copy–>Copy as cURL(bash) 打开postman 复制地址 进行发送。 …

CF1717 D. Madoka and The Corruption Scheme [思维题?]

传送门:CF [前题提要]:近期在集中刷1900的题,原本感觉这类题的思维难度对自己来说似乎没什么大问题,拿到手之后就开始乱贪心,然后就Wa4了,狠狠地被这道题给教育了,故记录一下 看了题解之后感觉这种做法之前在某道题中碰到过类似的,但是想不起来了… 我个人认为这道题的关键点…