理解 HTML5 Canvas 中逻辑像素与物理像素的关系
理解 HTML5 Canvas 中逻辑像素与物理像素的关系
在使用 HTML5 Canvas 时,开发者经常会遇到一个困惑:为什么鼠标的 offsetX 和 offsetY 和我绘制的图形坐标对不上?这通常是因为 Canvas 的逻辑像素大小和物理像素大小不一致。本文将详细解释这个问题,并给出通用解决方案。
什么是逻辑像素和物理像素?
逻辑像素
- 是 Canvas 内部的绘图坐标系大小,由 Canvas 元素的 width和height属性决定。
- 例如:
 上面的代码定义了一个 400x400 的逻辑像素大小。绘图时坐标范围为<canvas width="400" height="400"></canvas>(0, 0)到(400, 400)。
物理像素
- 是 Canvas 在网页中实际显示的尺寸,由 CSS 样式的 width和height控制。
- 例如:
 上面的代码将 Canvas 的显示缩小了一半,视觉上它是一个 200x200 的区域,但绘图坐标仍然是<canvas width="400" height="400" style="width: 200px; height: 200px;"></canvas>(0, 0)到(400, 400)。
为什么鼠标事件会出现问题?
当鼠标点击 Canvas 时,offsetX 和 offsetY 是基于 物理像素 的鼠标位置,而绘图坐标是基于 逻辑像素 的。两者之间的比例由 Canvas 的逻辑大小和 CSS 显示大小的关系决定。
例如:
<canvas width="400" height="400" style="width: 200px; height: 200px;"></canvas>
- 逻辑像素:绘制一个矩形,坐标为 (50, 50, 100, 100)。
- 物理像素:鼠标点击位置为 (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 样式。
解决方案
-  设置高分辨率 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); // 按设备像素比缩放绘图
-  鼠标事件仍然适用缩放比例,无需额外调整。 
总结
核心点
-  逻辑像素 vs. 物理像素 - 逻辑像素由 width和height定义,用于绘图。
- 物理像素由 CSS 控制,影响显示大小。
 
- 逻辑像素由 
-  鼠标事件坐标映射 - 使用 getBoundingClientRect()获取物理大小。
- 根据缩放比例调整 offsetX和offsetY。
 
- 使用 
-  适配高分辨率屏幕 - 提升逻辑像素分辨率。
- 使用 scale方法确保绘图清晰。
 
代码复用性
- 无论是适配高分辨率屏幕,还是处理鼠标事件,计算逻辑和物理像素缩放比例是通用的解决方案。这个方法不仅适用于 Canvas,也适用于其他需要精确像素计算的场景。
希望本文能帮助你更好地理解和解决 Canvas 中的坐标问题! 🎨
相关文章:
理解 HTML5 Canvas 中逻辑像素与物理像素的关系
理解 HTML5 Canvas 中逻辑像素与物理像素的关系 在使用 HTML5 Canvas 时,开发者经常会遇到一个困惑:为什么鼠标的 offsetX 和 offsetY 和我绘制的图形坐标对不上?这通常是因为 Canvas 的逻辑像素大小和物理像素大小不一致。本文将详细解释这…...
 
7.揭秘C语言输入输出内幕:printf与scanf的深度剖析
揭秘C语言输入输出内幕:printf与scanf的深度剖析 C语言往期系列文章目录 往期回顾: VS 2022 社区版C语言的安装教程,不要再卡在下载0B/s啦C语言入门:解锁基础概念,动手实现首个C程序C语言概念之旅:解锁关…...
 
数据分析-系统认识数据分析
目录 数据分析的全貌 观测 实验 应用 数据分析的全貌 观测 实验 应用...
蓝桥杯介绍
赛事背景与历程 自2009年举办以来,蓝桥杯已经连续举行了多届,成为国内领先的信息技术赛事。2022年,蓝桥杯被教育部确定为2022—2025学年面向中小学生的全国性竞赛活动,并入选国家级A类学科竞赛。 参赛对象与组别 蓝桥杯的参赛对…...
 
鸿蒙加载网络图片并转换成PixelMap
鸿蒙加载网络图片并转换成PixelMap 参考文档 基于API12. 有一些图片功能需要使用 PixelMap 类型的参数,但是使用Image组件之类的时候无法获取到 PixelMap 类型数据。 因此只能是把图片下载下来然后加在并转换一下。 实现方式 一下封装了一个函数。使用的 rcp 模…...
 
hive搭建
1.准备环境 三台节点主机已安装hadoopmysql数据库 2.环境 2.1修改三台节点上hadoop的core-site.xml <!-- 配置 HDFS 允许代理任何主机和组 --> <property><name>hadoop.proxyuser.hadoop.hosts</name><value>*</value> </property&…...
 
51c扩散模型~合集1
我自己的原文哦~ https://blog.51cto.com/whaosoft/11541675 #Diffusion Forcing 无限生成视频,还能规划决策,扩散强制整合下一token预测与全序列扩散 当前,采用下一 token 预测范式的自回归大型语言模型已经风靡全球,同时互联…...
 
从零开始深度学习:全连接层、损失函数与梯度下降的详尽指南
引言 在深度学习的领域,全连接层、损失函数与梯度下降是三块重要的基石。如果你正在踏上深度学习的旅程,理解它们是迈向成功的第一步。这篇文章将从概念到代码、从基础到进阶,详细剖析这三个主题,帮助你从小白成长为能够解决实际…...
Liebherr利勃海尔 EDI 需求分析
Liebherr 使用 EDI 技术来提高业务流程的效率、降低错误率、加快数据交换速度,并优化与供应商、客户和其他合作伙伴之间的业务沟通。通过 EDI,Liebherr 实现了与全球交易伙伴的自动化数据交换,提升了供应链管理和订单处理的透明度。 Liebher…...
java小练习
小练1.用while语句计算11/2!1/3!1/4!...1/20!的和 public class test_11_17_2 {public static void main(String[] args) {double sum 0;double item 1;int n 20;int i 1;while(i<n){sum item;i i1;item item*(1.0/i);}System.out.println(sum);} } 小练2.计算88888…...
go语言中的占位符有哪些
在Go语言中,占位符主要用于格式化字符串输出,特别是在使用fmt包中的Printf系列函数时。以下是Go语言中常用的占位符: %v:代表值的默认格式,对于字符串是直接输出,对于整型是十进制形式。%v:扩展…...
基于Windows安装opus python库
项目中需要用到一些opus格式的编解码功能,找到网上有opus的开源库。网址:Opus Codec 想着人生苦短,没想到遇上了错误!在这里记录一下过程 过程 安装python库 pip3 install opuslib验证 >>> import opuslib Tracebac…...
 
【设计模式】行为型模式(五):解释器模式、访问者模式、依赖注入
《设计模式之行为型模式》系列,共包含以下文章: 行为型模式(一):模板方法模式、观察者模式行为型模式(二):策略模式、命令模式行为型模式(三):责…...
 
使用nossl模式连接MySQL数据库详解
使用nossl模式连接MySQL数据库详解 摘要一、引言二、nossl模式概述2.1 SSL与nossl模式的区别2.2 选择nossl模式的场景三、在nossl模式下连接MySQL数据库3.1 准备工作3.2 C++代码示例3.3 代码详解3.3.1 初始化MySQL连接对象3.3.2 连接到MySQL数据库3.3.3 执行查询操作3.3.4 处理…...
 
【MySQL】ubantu 系统 MySQL的安装与免密码登录的配置
🍑个人主页:Jupiter. 🚀 所属专栏:MySQL初阶探索:构建数据库基础 欢迎大家点赞收藏评论😊 目录 📚mysql的安装📕MySQL的登录🌏MySQL配置免密码登录 📚mysql的…...
 
高级 SQL 技巧讲解
 大家好,我是程序员小羊! 前言: SQL(结构化查询语言)是管理和操作数据库的核心工具。从基本的查询语句到复杂的数据处理,掌握高级 SQL 技巧不仅能显著提高数据分析的效率,还能解决业务中的复…...
 
浅论AI大模型在电商行业的发展未来
随着人工智能(AI)技术的快速发展,AI大模型在电商行业中扮演着越来越重要的角色。本文旨在探讨AI大模型如何赋能电商行业,包括提升销售效率、优化用户体验、增强供应链管理等方面。通过分析AI大模型在电商领域的应用案例和技术进展…...
【python笔记03】《类》
文章目录 面向对象基本概念对象的概念类的概念 类的定义类的创建(实例的模板)类的实例化--获取对象对象方法中的self关键字面试题请描述什么是对象,什么是类。请观阅读如下代码,判断是否能正常运行,如果不能正常运行&a…...
Flutter 应用在真机上调试的流程
在真机上调试 Flutter 应用的方法有很多,可以使用 USB 数据线连接设备到电脑进行调试,也可以通过无线方式进行 Flutter 真机调试。 1. 有线调试 设备准备 启用开发者模式: Android:进入 设置 > 关于手机,连续点击…...
以太坊基础知识结构详解
以太坊的历史和发展 初创阶段 2013年:Vitalik Buterin 发表了以太坊白皮书,提出了一个通用的区块链平台,不仅支持比特币的货币功能,还能支持更复杂的智能合约。2014年:以太坊项目启动,进行了首次ICO&…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
 
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
 
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
 
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
 
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
 
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
