理解 HTML5 Canvas 中逻辑像素与物理像素的关系

理解 HTML5 Canvas 中逻辑像素与物理像素的关系

在使用 HTML5 Canvas 时,开发者经常会遇到一个困惑:为什么鼠标的 offsetXoffsetY 和我绘制的图形坐标对不上?这通常是因为 Canvas 的逻辑像素大小和物理像素大小不一致。本文将详细解释这个问题,并给出通用解决方案。


什么是逻辑像素和物理像素?

逻辑像素

  • 是 Canvas 内部的绘图坐标系大小,由 Canvas 元素的 widthheight 属性决定。
  • 例如:
    <canvas width="400" height="400"></canvas>
    
    上面的代码定义了一个 400x400 的逻辑像素大小。绘图时坐标范围为 (0, 0)(400, 400)

物理像素

  • 是 Canvas 在网页中实际显示的尺寸,由 CSS 样式的 widthheight 控制。
  • 例如:
    <canvas width="400" height="400" style="width: 200px; height: 200px;"></canvas>
    
    上面的代码将 Canvas 的显示缩小了一半,视觉上它是一个 200x200 的区域,但绘图坐标仍然是 (0, 0)(400, 400)

为什么鼠标事件会出现问题?

当鼠标点击 Canvas 时,offsetXoffsetY 是基于 物理像素 的鼠标位置,而绘图坐标是基于 逻辑像素 的。两者之间的比例由 Canvas 的逻辑大小和 CSS 显示大小的关系决定。

例如:

<canvas width="400" height="400" style="width: 200px; height: 200px;"></canvas>
  1. 逻辑像素:绘制一个矩形,坐标为 (50, 50, 100, 100)
  2. 物理像素:鼠标点击位置为 (100, 100),但因为显示缩小了一半,offsetX 实际代表逻辑坐标的 (200, 200)

结果:鼠标事件坐标和图形坐标完全对不上!


如何解决?

解决问题的关键是计算 逻辑坐标和物理坐标之间的缩放比例,然后对鼠标事件的坐标进行调整。

通用解决方案:动态计算缩放比例

通过 Canvas 的 getBoundingClientRect() 方法,可以获取 Canvas 在页面中的物理尺寸,再结合其逻辑大小计算缩放比例。

以下是完整代码实现:

handleMouseDown(e) {const dom = this.$refs.canvasRef; // 获取 Canvas DOMconst rect = dom.getBoundingClientRect(); // 获取物理大小// 计算缩放比例const scaleX = dom.width / rect.width; // X 轴缩放比例const scaleY = dom.height / rect.height; // Y 轴缩放比例// 调整鼠标坐标const offsetX = e.offsetX * scaleX;const offsetY = e.offsetY * scaleY;console.log(`Adjusted Mouse Position: (${offsetX}, ${offsetY})`);
}

完整案例:支持高分辨率与 CSS 缩放的 Canvas 鼠标交互

以下是一个完整的 Canvas 鼠标点击交互案例,解决逻辑像素和物理像素不一致的问题。

HTML

<canvas id="myCanvas" width="800" height="800" style="width: 400px; height: 400px; border: 1px solid black;"></canvas>

JavaScript

const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");// 绘制一个矩形
ctx.fillStyle = "blue";
ctx.fillRect(200, 200, 200, 200);// 监听鼠标点击事件
canvas.addEventListener("mousedown", (e) => {// 获取物理大小const rect = canvas.getBoundingClientRect();// 计算缩放比例const scaleX = canvas.width / rect.width;const scaleY = canvas.height / rect.height;// 调整鼠标坐标const offsetX = e.offsetX * scaleX;const offsetY = e.offsetY * scaleY;console.log(`Mouse Logical Position: (${offsetX}, ${offsetY})`);// 检测点击是否在矩形内if (offsetX >= 200 && offsetX <= 400 &&offsetY >= 200 && offsetY <= 400) {alert("You clicked inside the rectangle!");}
});

适配高分辨率屏幕(Retina 屏幕)

在高分辨率屏幕上,Canvas 的默认显示分辨率可能不足,导致图形模糊。为了解决这个问题,可以在逻辑像素上增加分辨率,同时按比例调整 CSS 样式。

解决方案

  1. 设置高分辨率 Canvas:

    const dpr = window.devicePixelRatio || 1;
    canvas.width = 400 * dpr; // 提高逻辑像素
    canvas.height = 400 * dpr;
    canvas.style.width = "400px"; // 设置 CSS 尺寸
    canvas.style.height = "400px";ctx.scale(dpr, dpr); // 按设备像素比缩放绘图
    
  2. 鼠标事件仍然适用缩放比例,无需额外调整。


总结

核心点

  1. 逻辑像素 vs. 物理像素

    • 逻辑像素由 widthheight 定义,用于绘图。
    • 物理像素由 CSS 控制,影响显示大小。
  2. 鼠标事件坐标映射

    • 使用 getBoundingClientRect() 获取物理大小。
    • 根据缩放比例调整 offsetXoffsetY
  3. 适配高分辨率屏幕

    • 提升逻辑像素分辨率。
    • 使用 scale 方法确保绘图清晰。

代码复用性

  • 无论是适配高分辨率屏幕,还是处理鼠标事件,计算逻辑和物理像素缩放比例是通用的解决方案。这个方法不仅适用于 Canvas,也适用于其他需要精确像素计算的场景。

希望本文能帮助你更好地理解和解决 Canvas 中的坐标问题! 🎨

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

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

相关文章

Nginx在Windows上和Linux上(Docker启动)分别配置基本身份认证示例

场景 Nginx代理的资源或网站等&#xff0c;url直接暴露有风险&#xff0c;需要添加身份认证&#xff0c;即输入用户名密码后才能成功访问。 注&#xff1a; 博客&#xff1a;霸道流氓气质-CSDN博客 实现 Windows上配置Nginx实现基本身份认证 修改nginx的配置文件 添加基…

丹摩征文活动|丹摩智算平台使用指南

目录 1. 登录平台与工作环境设置1.1 访问与登录1.2 创建或选择项目1.3 初始化项目环境 2. 数据上传与管理2.1 数据上传2.2 数据管理与预处理2.3 数据可视化 3. 模型构建与训练3.1 模型选择3.2 参数配置3.3 模型训练与评估 4. 模型部署与应用4.1 模型部署4.2 接口调用与集成4.3 …

用MVVM设计模式提升WPF开发体验:分层架构与绑定实例解析

MVVM&#xff08;Model-View-ViewModel&#xff09;是一种架构模式&#xff0c;广泛应用于现代前端开发&#xff0c;尤其是在微软的WPF&#xff08;Windows Presentation Foundation&#xff09;应用程序中。它旨在通过将视图&#xff08;UI&#xff09;与业务逻辑&#xff08;…

std::memory_order 多线程编程中的内存顺序

std::memory_order 是 C11 引入的一个枚举&#xff0c;用于控制多线程编程中的内存顺序。它定义了在原子操作中对内存的可见性和排序的要求。它在 C 标准库的 <atomic> 头文件中&#xff0c;配合原子类型和操作&#xff08;如 std::atomic 和 std::atomic_flag&#xff0…

MySql--多表查询及聚合函数总结

建议先阅读MySql--增删改查表设计总结-CSDN博客 一、聚合函数 1.COUNT(列||*)&#xff1a;统计结果的个数。 2.SUM&#xff08;列&#xff09;&#xff1a;求和。 3.AVG(列)&#xff1a;求平均值。 4.MIN(列) 最小值。 5.MAX(列) &#xff1a;最大值。 二、GROUP BY 分组查询…

【数据库实验一】数据库及数据库中表的建立实验

目录 实验1 学习RDBMS的使用和创建数据库 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六、实验结果 七、评价分析及心得体会 实验2 定义表和数据库完整性 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六…

前端 JS面向对象 原型 prototype

目录 一、问题引出 二、prototype原型对象 三、小结 四、constructor 五、__proto__对象原型 六、原型链 一、问题引出 由于JS的构造函数存在内存浪费问题&#xff1a; function Star(name,age){this.namenamethis.ageagethis.singfunction () {console.log("唱歌&…

NodeJS的安装 npm 配置和使用 Vue-cli安装 Vue项目介绍

一.前端工程化 前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的为了提高效率和降低成本 1. NodeJS的安装 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环 境&#xff0c;可以使 JavaScript 运行在服务…

Linux学习,ssh 命令

SSH&#xff08;Secure Shell&#xff09;是一种网络协议&#xff0c;通过加密的方式在客户端和服务器之间建立安全的远程连接。Linux系统中的SSH命令是用于通过SSH协议远程登录和管理其他计算机的重要工具。SSH (Secure Shell) 是一种用于远程登录和其他网络服务之间的加密协议…

「Mac玩转仓颉内测版7」入门篇7 - Cangjie控制结构(下)

本篇继续深入介绍 for-in 循环的用法&#xff0c;并探讨 break 和 continue 控制结构的应用&#xff0c;帮助优化程序流程。 关键词 Cangjie控制结构Cangjie循环语句for-in控制转移程序优化 一、for-in 表达式 for-in 表达式用于遍历扩展了迭代器接口 Iterable 的类型实例。…

软件工程概论项目(二),node.js的配置,npm的使用与vue的安装

上一章我们配置了git仓库&#xff0c;这一章我们来配置项目需要用的一些其他的环境。 放一个思维导图在这里&#xff0c;可以参考一下&#xff0c;很不全面&#xff0c;没有参考价值,反正我先这样写吧。 参考了这个nodejs的配置&#xff0c;写的很好&#xff1a;https://blog.c…

什么是crm?3000字详细解析

在现代商业环境中&#xff0c;客户关系管理&#xff08;CRM&#xff09;已经成为企业驱动成功的关键工具。在复杂且竞争激烈的市场中&#xff0c;如何有效地管理客户关系、提升客户满意度&#xff0c;并增加客户忠诚度&#xff0c;越来越成为企业迫切关心的问题。而CRM系统&…

中心极限定理的三种形式

独立同分布的中心极限定理&#xff1a; 设 X 1 , X 2 , … , X n X_1, X_2, \ldots, X_n X1​,X2​,…,Xn​是独立同分布的随机变量序列&#xff0c;且 E ( X i ) μ E(X_i) \mu E(Xi​)μ&#xff0c; D ( X i ) σ 2 > 0 D(X_i) \sigma^2 > 0 D(Xi​)σ2>0存在…

3.1_文件上传漏洞

文件上传漏洞 文件上传漏洞原理&#xff1a;未对用户提交的文件进行严格校验&#xff0c;就将恶意文件解析执行&#xff0c;导致用户可以提交恶意的文件进行攻击&#xff1b; 利用方式&#xff08;危害&#xff09;&#xff1a; 1&#xff09;. 上传 HTML/SVG 进行 XSS 攻击&…

C++入门基础知识140—【关于C++ 类构造函数 析构函数】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 类构造函数 & 析构函数的相关内容…

前端框架大比拼:React.js, Vue.js 及 Angular 的优势与适用场景探讨

文章目录 前言一、React.js特点使用方法适用场景 二、Vue.js特点使用方法适用场景 三、Angular特点使用方法适用场景 四、如何选择合适的前端框架五、前端框架对项目性能的影响结语 前言 随着互联网技术的飞速发展&#xff0c;前端开发已经从简单的页面展示演变为复杂的应用构…

Notepad++的完美替代

由于Notepad的作者曾发表过可能在开发者代码中植入恶意软件的言论&#xff0c;他备受指责。在此&#xff0c;我向大家推荐一个Notepad的完美替代品——NotepadNext和Notepad--。 1、NotepadNext NotepadNext的特点&#xff1a; 1、跨平台兼容性 NotepadNext基于Electron或Qt…

为什么RNN(循环神经网络)存在梯度消失和梯度爆炸?

1️⃣ 原理分析 RNN前向传播的公式为&#xff1a; x t x_t xt​是t时刻的输入 s t s_t st​是t时刻的记忆&#xff0c; s t f ( U ⋅ x t W ⋅ s t − 1 ) s_tf(U\cdot x_tW\cdot s_{t-1}) st​f(U⋅xt​W⋅st−1​)&#xff0c;f表示激活函数&#xff0c; s t − 1 s_{t-1} …

NUXT3学习日记二(样式配置、引入组件库、区分在服务端还是在客户端渲染)

上一章已经给大家分享官网下载的nuxt3了&#xff0c;下面正式进入我所要说的内容吧 一、初始化样式 想必大家从我的git下载下来的nuxt3&#xff0c;能看到nuxt.config.ts这个文件了吧。 这里我们有两种css配置方式 1、css:[~/assets/base.scss] 这种方式不能在scss文件中定义…

2024AAAI | DiffRAW: 利用扩散模型从手机RAW图生成单反相机质量的RGB图像

文章标题&#xff1a;《DiffRAW: Leveraging Diffusion Model to Generate DSLR-Comparable Perceptual Quality sRGB from Smartphone RAW Images》 原文链接&#xff1a;DiffRAW 本文是清华大学深圳研究院联合华为发表在AAAI-2024上的论文&#xff08;小声bb&#xff1a;华…