首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

canvas实现手动绘制矩形

编程知识
2024年08月15日 08:19

开场白

虽然在实际的开发中我们很少去绘制流程图
就算需要,我们也会通过第3方插件去实现
下面我们来简单实现流程图中很小的一部分
手动绘制矩形

绘制一个矩形的思路

我们这里绘制矩形
会使用到canvas.strokeRect(x,y, w, h)方法绘制一个描边矩形
x:矩形起点的 x 轴坐标。
y:矩形起点的 y 轴坐标。
width:矩形的宽度。正值在右边,负值在左边。
height:矩形的高度。正值下降,负值上升。
注意一下w,h这两个参数的正直和负值。
如果w,h是负数,会出现了2个斜着对称的矩形

绘制一个静态矩形

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    html,body{
      /* 去除浏览器内置的margin */
      margin: 0;
      /* 整个页面铺满 */
      height: 100%;
      width: 100%;
    }
  </style>
</head>
<body>
  <canvas></canvas>
</body>
<script>
  // 获取canvas元素
  let canvasEle = document.getElementById('canvas')
  // 获取canvas的上下文
  const ctx = canvasEle.getContext('2d')
  // 设置canvas的大小(宽高) 与屏幕一样宽高
  const screenSize = document.documentElement
  canvasEle.width =  window.screen.availWidth
  canvasEle.height =  window.screen.availHeight
  // 给矩形的设置颜色,设置颜色一定要在绘制之前,否则将会不生效
  ctx.strokeStyle = '#a0a'
  // 绘制一个路径模式是的矩形,起始坐标100,100, 宽300,高280
  ctx.strokeRect(100,100,300,280)
</script>
</html>

手动绘制矩形的基本思路

通过上面我们实现了静态绘制矩形。
如果我们想要手动画一个矩形,需要实现以下几个步骤
1.给canvas注册鼠标按下事件,在按下的时候记录矩形的起始坐标(x,y)
与此同时,还需要在按下时注册鼠标移动事件和抬起事件。
2.在鼠标移动的时候,通过计算得到矩形的宽和高。
计算矩形的宽度和高度时,我们要使用绝对值进行计算。
计算后立即绘制矩形
3.在鼠标抬起时,移除之前注册的鼠标移动事件和抬起事件

手动绘制矩形

<script>
// 获取canvas元素 oCan
let canvasEle = document.getElementById('canvas')
// 获取canvas的上下文
const ctx = canvasEle.getContext('2d')
// 设置canvas的大小(宽高) 与屏幕一样宽高
const screenSize = document.documentElement
canvasEle.width =  window.screen.availWidth
canvasEle.height =  window.screen.availHeight
// 所有的矩形信息
let rectArr = []
// 给canvas注册事件按下事件
canvasEle.addEventListener('mousedown',canvasDownHandler)
function canvasDownHandler(e){
  console.log('按下', e)
  rectArr = [e.clientX,e.clientY ]
  // 按下的时候需要注册移动事件
  canvasEle.addEventListener('mousemove', canvasMoveHandler)
  // 抬起事件
  canvasEle.addEventListener('mouseup', canvasMouseUpHandler)
}
// 移动的时候我们需要记录起始点和结束点,然后就可以绘制矩形了
function canvasMoveHandler(e){
  console.log('我们在移动了', e)
  // 在移动的时候就开始绘制矩形
  drawRect(rectArr[0], rectArr[1], e.clientX, e.clientY)
}

function drawRect(x1,y1,x2,y2){
  // 在计算矩形的宽高时,我们需要使用绝对值来进行计算
  // 此时此刻移动的坐标减去最初按下的坐标就是矩形的宽和高
  // 如果不用绝对值,可能会出现2个矩形
  let rectWidth = Math.abs(x2-x1)
  let rectHeight = Math.abs(y2-y1)
  // 存储矩形的坐标信息
  rectArr = [x1,y1,rectWidth,rectHeight]
  ctx.strokeRect(...rectArr)
}
// 当我们鼠标抬起的时候要移除之前注册移动事件和抬起事件
function canvasMouseUpHandler(){
  canvasEle.removeEventListener('mousemove', canvasMoveHandler)
  canvasEle.removeEventListener('mouseup', canvasMouseUpHandler)
}
</script>


发现问题出现多余的路径

通过上面这张图片,我们虽然绘制手动绘制出了矩形。
但是出现了重复的路径。
我们先分析一下出现重复路径的原因。
我们在每次移动的过程中,都会绘制矩形。
只要我们在绘制前,清空矩形是不是就可以解决这个问题
我们来尝试一下

在绘之前清除多余的路径

function drawRect(x1,y1,x2,y2){
  // 在计算矩形的宽高时,我们需要使用绝对值来进行计算
  // 此时此刻移动的坐标减去最初按下的坐标就是矩形的宽和高
  let rectWidth = Math.abs(x2-x1)
  let rectHeight = Math.abs(y2-y1)
  // 存储矩形的坐标信息
  rectArr = [x1,y1,rectWidth,rectHeight]
  // 在绘制矩形前,我们将矩形清空,然后在绘制,就不会出现多余的路径了
  ctx.clearRect(0,0,  canvasEle.width, canvasEle.height)
  // 重新绘制矩形
  ctx.strokeRect(...rectArr)
}

机智的小伙伴发现问题了?

有些机智的小伙伴发现:
如果我起始点是右下角,终点是左上角。
即:用户从(900, 1000)拖动到(50, 50)这种情况
按照这样的方向绘制矩形,是不是会出现问题呢?
确实会出现问题。
此时绘制的矩形不会随着我们的方向进行绘制。请看下面的图
如何处理这个问题呢?

主角闪亮登场 canvas.rect

canvas.rect(x,y,w,h)该方法创建一个矩形路径
x:矩形起点的 x 轴坐标。
y:矩形起点的 y 轴坐标。
width:矩形的宽度。正值在右边,负值在左边。
height:矩形的高度。正值下降,负值上升。
这个方法是创建一个矩形路径,创建的路径并不会直接绘制在画布上。
需要调用stroke()或fill()才能显示在画布上。

使用canvas.rect 绘制矩形路径

function drawRect(x1,y1,x2,y2){
  let rectWidth = Math.abs(x2-x1)
  let rectHeight = Math.abs(y2-y1)
  // 绘制之前先清空之前实时移动产生的多余的矩形路径
  ctx.clearRect(0,0,  canvasEle.width, canvasEle.height)
  // 开始画线
  ctx.beginPath();  
  // 绘制路径矩形
  ctx.rect(Math.min(x1, x2), Math.min(y1, y2), rectWidth, rectHeight);  
  // 绘制形状的轮廓。
  ctx.stroke(); 
}

canvas.strokeRect与canvas.rect的异同

区别1功能性: canvas.strokeRect:绘制的是边框。canvas.rect创建矩形的路径(不绘立即绘制在矩形上)
区别2即时性: canvas.strokeRect是立即绘制。canvas.rect不是立即绘制,需要调用stroke()或fill()才能绘制
相同点:
1.都是绘制矩形
2.接受的参数相同
3.都是通过strokeStyle(颜色)和lineWidth(线的粗细)等来设置样式

连续绘制多个矩形

function drawRect(x1,y1,x2,y2){
  let rectWidth = Math.abs(x2-x1)
  let rectHeight = Math.abs(y2-y1)
  let endX = Math.min(x1, x2)
  let endY = Math.min(y1, y2)
  // 绘制之前先清空之前实时移动产生的多余的矩形路径
  ctx.clearRect(0,0,  canvasEle.width, canvasEle.height)
  // 绘制之前那些存储在 beforeRectArr 数组中的矩形
  allRectInfoArr = [endX, endY, rectWidth, rectHeight]
  ctx.clearRect(0,0,  canvasEle.width, canvasEle.height)
  beforeRectArr.forEach(element => {
    ctx.beginPath(); 
    ctx.strokeRect(...element)
    ctx.stroke();
  });
  
  // 开始本次路径
  ctx.beginPath(); 
  // 绘制本次的矩形路径
  ctx.rect(...allRectInfoArr); 
  // 开始填充矩形
  ctx.stroke(); 
}
// 当我们鼠标抬起的时候要移除之前注册移动事件和抬起事件
function canvasMouseUpHandler(){
  savaBeforeRect()
  canvasEle.removeEventListener('mousemove', canvasMoveHandler)
  canvasEle.removeEventListener('mouseup', canvasMouseUpHandler)
}
function savaBeforeRect(){
  beforeRectArr.push(allRectInfoArr)
}

全部代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    html,body{
      /* 去除浏览器内置的margin */
      margin: 0;
      /* 整个页面铺满 */
      height: 100%;
      width: 100%;
    }
  </style>
</head>
<body>
  <canvas></canvas>
</body>
<script>
  // 获取canvas元素 oCan
  let canvasEle = document.getElementById('canvas')
  // 获取canvas的上下文
  const ctx = canvasEle.getContext('2d')
  // 设置canvas的大小(宽高) 与屏幕一样宽高
  const screenSize = document.documentElement
  canvasEle.width =  window.screen.availWidth
  canvasEle.height =  window.screen.availHeight
  // 给矩形的设置颜色
  // ctx.strokeStyle = '#a0a'
  // // 绘制一个路径模式是的矩形,起始坐标100,100, 宽300,高280
  // ctx.strokeRect(100,100,400,280)
  // 矩形信息
  let rectArr = []
  // 所有的矩形信息
  allRectInfoArr = []
  let beforeRectArr =[]
  // 给canvas注册事件按下事件
  canvasEle.addEventListener('mousedown',canvasDownHandler)
  function canvasDownHandler(e){
    rectArr = [e.clientX,e.clientY ]
    // 按下的时候需要注册移动事件
    canvasEle.addEventListener('mousemove', canvasMoveHandler)
    // 抬起事件
    canvasEle.addEventListener('mouseup', canvasMouseUpHandler)
  }
  // 移动的时候我们需要记录起始点和结束点,然后就可以绘制矩形了
  function canvasMoveHandler(e){
    // 在移动的时候就开始绘制矩形
    drawRect(rectArr[0], rectArr[1], e.clientX, e.clientY)
  }

  function drawRect(x1,y1,x2,y2){
    let rectWidth = Math.abs(x2-x1)
    let rectHeight = Math.abs(y2-y1)
    let endX = Math.min(x1, x2)
    let endY = Math.min(y1, y2)
    // 绘制之前先清空之前实时移动产生的多余的矩形路径
    ctx.clearRect(0,0,  canvasEle.width, canvasEle.height)
    // 绘制之前那些存储在 beforeRectArr 数组中的矩形
    allRectInfoArr = [endX, endY, rectWidth, rectHeight]
    beforeRectArr.forEach(element => {
      ctx.beginPath(); 
      ctx.strokeRect(...element)
      ctx.stroke();
    });
    
    // 开始本次路径
    ctx.beginPath(); 
    // 绘制本次的矩形路径
    ctx.rect(...allRectInfoArr); 
    // 开始填充矩形
    ctx.stroke(); 
  }
  // 当我们鼠标抬起的时候要移除之前注册移动事件和抬起事件
  function canvasMouseUpHandler(){
    savaBeforeRect()
    canvasEle.removeEventListener('mousemove', canvasMoveHandler)
    canvasEle.removeEventListener('mouseup', canvasMouseUpHandler)
  }

  function savaBeforeRect(){
    beforeRectArr.push(allRectInfoArr)
  }
</script>
</html>

尾声

如果小伙伴觉得我写的不错的话
可以给我点个赞吗?感谢了。
后面会继续写:
如何选中矩形,更改矩形大小。
如何在矩形上添加文字。
如何绘制圆,箭头符号等

From:https://www.cnblogs.com/IwishIcould/p/18360209
本文地址: http://shuzixingkong.net/article/1115
0评论
提交 加载更多评论
其他文章 《痞子衡嵌入式半月刊》 第 106 期
痞子衡嵌入式半月刊: 第 106 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿。 上期回
《痞子衡嵌入式半月刊》 第 106 期 《痞子衡嵌入式半月刊》 第 106 期 《痞子衡嵌入式半月刊》 第 106 期
Binance 如何使用 Quickwit 构建 100PB 日志服务(Quickwit 博客)
三年前,我们开源了 Quickwit,一个面向大规模数据集的分布式搜索引擎。我们的目标很宏大:创建一种全新的全文搜索引擎,其成本效率比 Elasticsearch 高十倍,配置和管理显著更简单,并且能够扩展到 PB 级别的数据。 https://quickwit.io/blog/quickwit-f
Binance 如何使用 Quickwit 构建 100PB 日志服务(Quickwit 博客) Binance 如何使用 Quickwit 构建 100PB 日志服务(Quickwit 博客) Binance 如何使用 Quickwit 构建 100PB 日志服务(Quickwit 博客)
如何在实验室信息管理系统实现不定行,不定列检测?
前言 实验室信息管理系统,即 LIMS(Laboratory Information Management System),它是由计算机和应用软件组成,能够完成实验室数据和信息的收集、分析、报告和管理。早期的 LIMS 系统大多基于计算机局域网,专门针对一个实验室的整体环境而设计,是一个包括了信号采
如何在实验室信息管理系统实现不定行,不定列检测? 如何在实验室信息管理系统实现不定行,不定列检测? 如何在实验室信息管理系统实现不定行,不定列检测?
《数据资产管理核心技术与应用》读书笔记-第四章:数据质量的技术实现(一)
《数据资产管理核心技术与应用》是清华大学出版社出版的一本图书,全书共分10章,第1章主要让读者认识数据资产,了解数据资产相关的基础概念,以及数据资产的发展情况。第2~8章主要介绍大数据时代数据资产管理所涉及的核心技术,内容包括元数据的采集与存储、数据血缘、数据质量、数据监控与告警、数据服务、数据权限
《数据资产管理核心技术与应用》读书笔记-第四章:数据质量的技术实现(一) 《数据资产管理核心技术与应用》读书笔记-第四章:数据质量的技术实现(一) 《数据资产管理核心技术与应用》读书笔记-第四章:数据质量的技术实现(一)
神经网络之卷积篇:详解三维卷积(Convolutions over volumes)
详解三维卷积 从一个例子开始,假如说不仅想检测灰度图像的特征,也想检测RGB彩色图像的特征。彩色图像如果是6&#215;6&#215;3,这里的3指的是三个颜色通道,可以把它想象成三个6&#215;6图像的堆叠。为了检测图像的边缘或者其他的特征,不是把它跟原来的3&#215;3的过滤器做卷积,而是跟
神经网络之卷积篇:详解三维卷积(Convolutions over volumes) 神经网络之卷积篇:详解三维卷积(Convolutions over volumes) 神经网络之卷积篇:详解三维卷积(Convolutions over volumes)
Deformable DETR:商汤提出可变型 DETR,提点又加速 | ICLR 2021 Oral
DETR能够消除物体检测中许多手工设计组件的需求,同时展示良好的性能。但由于注意力模块在处理图像特征图方面的限制,DETR存在收敛速度慢和特征分辨率有限的问题。为了缓解这些问题,论文提出了Deformable DETR,其注意力模块仅关注参考点周围的一小组关键采样点,通过更少的训练次数实现比DETR
Deformable DETR:商汤提出可变型 DETR,提点又加速 | ICLR 2021 Oral Deformable DETR:商汤提出可变型 DETR,提点又加速 | ICLR 2021 Oral Deformable DETR:商汤提出可变型 DETR,提点又加速 | ICLR 2021 Oral
PHP转Go系列 | ThinkPHP与Gin框架之打造基于WebSocket技术的消息推送中心
在早些年前客户端想要实时获取到最新消息,都是使用定时长轮询的方式,不断的从服务器上获取数据,这种粗暴的骚操作实属不雅。
PHP转Go系列 | ThinkPHP与Gin框架之打造基于WebSocket技术的消息推送中心 PHP转Go系列 | ThinkPHP与Gin框架之打造基于WebSocket技术的消息推送中心 PHP转Go系列 | ThinkPHP与Gin框架之打造基于WebSocket技术的消息推送中心
优化系统性能:深入探讨Web层缓存与Redis应用的挑战与对策
Web层缓存显著提高了应用性能,通过减少重复的数据处理和数据库查询来加快响应时间。Redis作为高效的内存数据结构存储系统,在实现缓存层中发挥了重要作用,它支持各种数据结构,能够迅速存取数据,从而减少数据库负担,提升用户体验。然而,缓存机制也面临挑战,如缓存穿透、缓存击穿和缓存雪崩等问题。缓存穿透通
优化系统性能:深入探讨Web层缓存与Redis应用的挑战与对策