HTML5 Canvas 为什么会模糊,以及如何修复
Canvas 有两个尺寸,两者很容易混淆:
- 显示尺寸——元素在页面上的大小,以 CSS 像素计。
- 位图尺寸——浏览器为绘制分配的真实像素数量。
当位图小于显示尺寸时,浏览器会拉伸结果,绘制内容看起来就发虚。在 Retina 屏幕上,每个 CSS 像素对应两到三个设备像素,因此每个 CSS 像素只分配一个位图像素的 Canvas 默认就会被拉伸。
Konva 已经处理了这一点
大多数建议都在这一点上出错,因此值得明确说明。
Konva.pixelRatio 默认取 window.devicePixelRatio,Konva 创建的每个 Canvas 都会按该比例
分配位图。你以 CSS 像素向 Konva 提供尺寸,Konva 会为你设置位图尺寸。
// This is correct on a retina screen. Nothing else is needed.
const stage = new Konva.Stage({
container: 'container',
width: 600, // CSS pixels
height: 400, // CSS pixels
});
在 2 倍屏幕上,该舞台绘制到 1200 × 800 的位图中,并以 600 × 400 显示。文本和描边都很清晰。
会让问题更糟的"修复"
搜索"canvas 模糊"会找到下面这种写法。它对原生 <canvas> 是正确的,对 Konva 则是错误的:
// DO NOT do this in Konva
const dpr = window.devicePixelRatio;
stage.width(container.clientWidth * dpr);
stage.height(container.clientHeight * dpr);
stage.scale({ x: dpr, y: dpr });
它把设备像素比乘了两次:一次在这里,一次在 Konva 内部。在 2 倍屏幕上,舞台最终会 比它的容器大一倍,并渲染到面积为所需四倍的位图中。绘制结果依然清晰,所以这个错误很容易 被忽略——你注意到的是场景太大,以及内存占用偏高。
这个建议本身并没有错。面对裸的 Canvas 上下文时,没有任何东西会替你设置位图尺寸,你就该这样写。 但 Konva 不是裸的上下文。
确实模糊的情况
如果 Konva 场景看起来仍然发虚,原因通常是下面几种之一。
容器被 CSS 缩放了
容器上的 transform: scale(),或者与舞台尺寸不匹配的百分比 width,都会拉伸已经绘制好的
位图。Konva 无法感知这一点。应当让舞台尺寸跟随容器,参见
响应式 Canvas。
像素比被关掉了
Konva.pixelRatio = 1 是一条真实有效的性能建议,代价是在 Retina 屏幕上损失清晰度。
对于繁重的场景值得这样做,对于文本则不值得。如果有人全局设置了它,那就是原因所在。
Konva.pixelRatio = 1; // faster, and soft on retina