【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 分钟前
详解 HTML Image 的 mode 属性:图像显示模式的灵活控制
前端·html·uniapp
爱吃无爪鱼35 分钟前
03-Bun vs Node.js:JavaScript 运行时的新旧之争
javascript·vue.js·react.js·npm·node.js
0思必得01 小时前
[Web自动化] 开发者工具性能(Performance)面板
运维·前端·自动化·web自动化·开发者工具
心灵的制造商1 小时前
el-tree左侧新增类别和删除类别实例代码
前端·javascript·vue.js
爱吃无爪鱼1 小时前
01-前端开发快速入门路线图
javascript·css·vue.js·typescript·前端框架·npm·node.js
冴羽1 小时前
不知道怎么写 Nano Banana Pro 提示词?分享你一个结构化示例,复刻任意图片
前端·人工智能·aigc
IT_陈寒1 小时前
JavaScript 性能优化:7个 V8 引擎隐藏技巧让你的代码提速200%
前端·人工智能·后端
脾气有点小暴1 小时前
uniapp通用单张图片上传组件
前端·javascript·vue.js·uni-app·uniapp
小菜今天没吃饱1 小时前
DVWA-XSS(stored)
前端·网络安全·xss·dvwa
云飞云共享云桌面1 小时前
研发部门使用SolidWorks,三维设计云桌面应该怎么选?
运维·服务器·前端·网络·自动化·电脑