【Canvas系列】Canvas 尺寸与分辨率矫正

前言

Canvas 的默认大小为 300 像素 ×150 像素(宽 × 高,像素的单位是 px)

html 复制代码
<canvas id="tutorial" width="150" height="150"></canvas>

canvas 看起来和 img 元素很相像,唯一的不同就是它并没有 src 和 alt 属性。实际上,canvas 标签只有两个属性: width 和 height。

正文

设置画布的 css 大小

canvas 可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果 CSS 的尺寸与初始画布的比例不一致,它会出现扭曲。

假设一个场景,当我们给 canvas 设置了 css 的宽高 500px * 500px, 但是我们在 canvas 中绘制的图形是 100px * 100px, 那么最终的图形会被拉伸,如下图所示:

html 复制代码
<canvas id="test-canvas"></canvas>
<script>
    const canvas = document.getElementById('test-canvas');
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = 'red';
    ctx.fillRect(0, 0, 100, 100);
    canvas.style.width = "500px";
    canvas.style.height = "500px";
</script>

因此,如果你想绘制一个 100px × 100px 的正方形,而且你的 CSS 代码中设置了 width: 500px; height: 500px;,那么最终图像将会是一个扁平的正方形。

设置画布的属性大小

从上面的案例我们发现,当我们设置了 canvas 的 css 大小后,canvas 会被拉伸,那么我们可以通过设置 canvas 的属性 width、height 来解决这个问题。

html 复制代码
<canvas id="test-canvas" width="500" height="500"></canvas>
<script>
    const canvas = document.getElementById('test-canvas');
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = 'red';
    ctx.fillRect(0, 0, 100, 100);
    canvas.style.width = "500px";
    canvas.style.height = "500px";
</script>

当我们设置了 canvas 的属性 width、height 后,canvas 的大小就固定了,不会被拉伸,但是我们在 canvas 中绘制的图形是 100px * 100px, 那么最终的图形也显示正常了

备注: 如果你绘制出来的图像是扭曲的,尝试用 width 和 height 属性为<canvas>明确规定宽高,而不是使用 CSS。

Canvas分辨率矫正

通过上面的案例,虽然我们的正方形没有变形,但是边框很模糊(图片截图可能看的不是很明显)。这是因为不同DPR下,绘制100px的图形所需要的像素不同。比如同样是100px的元素,如果在DPR为1的屏幕上,只需要100的像素,此时就很清晰。但是如果在DPR为2的屏幕上,就需要200像素,但实际上canvas绘制时,只绘制了100px。具体可以看这里。下面的代码就是用于矫正canvas的分辨率:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas 实验</title>
    <style type="text/css">
        canvas {
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <canvas id="test-canvas" width="500" height="500"></canvas>
    <script>
        const dpr = window.devicePixelRatio || 1;

        console.log(`dpr--->`, dpr);
        const canvas = document.getElementById('test-canvas');
        const ctx = canvas.getContext('2d');
        canvas.style.width = "500px";
        canvas.style.height = "500px";
        canvas.width = 500 * dpr;
        canvas.height = 500 * dpr;
        ctx.scale(dpr, dpr);
        ctx.strokeRect(0, 0, 100, 100);
    </script>
</body>
</html>

当我们将 canvas 的尺寸乘上像素比后,再将 canvas 的尺寸缩小到原来的 css 尺寸,这样就可以保证 canvas 1px 绘制所需要的像素一致,从而保证绘制出来的图形不会模糊。

监听 dpr 变化

当用户进行页面切换或者将浏览器放置到其他屏幕时,dpr 可能会发生变化,因此我们需要监听 dpr 的变化,从而重新绘制 canvas。

由于在下使用的技术栈是 React。因此通过监听 window 的 change 事件,从而重新调整 canvas 的尺寸。

js 复制代码
export function useDevicePixelRatio() {
    const [dpr, setDpr] = useState(window.devicePixelRatio || 1);
    useEffect(() => {
        const update = () => {
            setDpr(window.devicePixelRatio || 1);
        };
        window.addEventListener('change', update);
        return () => {
            window.removeEventListener('change', update);
        };
    }, [dpr]);
}

<canvas
    width={width * dpr}
    height={height * dpr}
    style={{
        width: `${width}px`,
        height: `${height}px`,
    }}
></canvas>

// 这时候我们还需要重新缩放 ctx
const ctx = canvas.getContext('2d');
ctx.setTransform(1, 0, 0, 1, 0, 0); // scale 前先恢复变换矩阵,不然会重复 scale
ctx.scale(dpr, dpr);

参考文档

相关推荐
一丝晨光22 分钟前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
Front思23 分钟前
vue使用高德地图
javascript·vue.js·ecmascript
zqx_71 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己1 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称2 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色2 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2342 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
NiNg_1_2342 小时前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript
读心悦2 小时前
如何在 Axios 中封装事件中心EventEmitter
javascript·http