Intro to Canvas
· 11 min read
画布尺寸
Canvas 有两种 width
和 height
:
- 一种是 width、height 属性,一般称其为 画布尺寸,即图形绘制的地方。默认值分别为
300px
、150px。
例如:
<canvas id="canvas" width="300" height="150"></canvas>
- 另一种是 CSS 样式里的 width、height 属性,可通过内联样式、内部样式表或外部样式表设置。一般称其为 画板尺寸,用于渲染绘制完成的图形。默认值为空。
例如:
<canvas id="canvas" style="width: 300px; height: 150px;"></canvas>
或
<style>
#canvas {
width: 300px;
height: 150px;
}
</style>
画布尺寸 | 画板尺寸 | 说明 |
---|---|---|
已设置 | 未设置 | 画板尺寸随画布尺寸改变 |
未设置 | 已设置 | 画板尺寸将不再随画布尺寸而改变 |
已设置 | 已设置 | 画板尺寸将不再随画布尺寸而改变 |
如果两者设置的尺寸不一样时,就会产生一个问题,渲染时画布要通过缩放来使其与画板尺寸一样,那么画布上已经绘制好的图形也会随之缩放,随之导致变形失真。
下面为绘制从原点到 200*200
的直线:
Live Editor
class Demo extends React.Component { componentDidMount() { const canvasList = document.querySelectorAll('.canvas'); canvasList.forEach(item => { const context = item.getContext('2d'); this.renderPath(context); }); } renderPath(ctx) { ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(200, 200); ctx.stroke(); } render() { return ( <> <p>1、已设置画布宽高属性(200px * 200px),未设置画板样式宽高,画板尺寸随画布尺寸改变</p> <canvas id="canvas1" className="canvas" width="200" height="200"></canvas> <p>2、未设置画布宽高属性,但已设置画板样式宽高(200px * 200px)</p> <canvas id="canvas2" className="canvas" style={{ width: 200, height: 200 }}></canvas> <p>3、设置画布宽高属性(300 * 300),设置画板样式宽高(200 * 200)</p> <canvas id="canvas3" className="canvas" width="300" height="300" style={{ width: 200, height: 200, border: '1px solid #000' }}></canvas> <p>4、设置画布宽高属性(200 * 200),设置画板样式宽高(300 * 300)</p> <canvas id="canvas4" className="canvas" width="200" height="200" style={{ width: 300, height: 300, border: '1px solid #000' }}></canvas> </> ); } }
Result
Loading...
高清分辨率
上面说过,避免图形变形失真,要保持画布尺寸和画板尺寸一致。
这只是针对分辨率不高的设备而言,其 window.devicePixelRatio
为 1。而高分辨率屏幕,它的 window.devicePixelRatio
大于 1。
Canvas 绘制的图形是位图,即 栅格图像 或 点阵图像,当将它渲染到高清屏时,会被放大,每个像素点会用 window.devicePixelRatio
平方个物理像素点来渲染,因此图片会变得模糊。
解决方法:
- 通过
window.devicePixelRatio
获取当前设备屏幕的 DPR - 获取或设置 Canvas 容器的画板尺寸
- 根据 DPR,设置 Canvas 元素的宽高属性(在 DPR 为 2 时,相当于扩大画布的两倍)
- 通过
context.scale(dpr, dpr)
缩放 Canvas 画布的坐标系,在 DPR 为 2 时相当于把 Canvas 坐标系也扩大了两倍,这样绘制比例放大了两倍,之后 Canvas 的实际绘制像素就可以按原先的像素值处理
Live Editor
function Demo() { const hdCanvasRef = useRef(null); const ldCanvasRef = useRef(null); const renderPath = context => { context.beginPath(); context.moveTo(0, 0); context.lineTo(200, 200); context.lineWidth = context.lineWidth; context.stroke(); }; // 绘制高清晰度画布 const initHDCanvas = () => { const canvas = hdCanvasRef.current; // 获取 DPR const dpr = window.devicePixelRatio; const ctx = canvas.getContext('2d'); // 获取容器高度 const { width, height } = hdCanvasRef.current.getBoundingClientRect(); // 根据 DPR 设置 Canvas 的宽高,使 1 个 Canvas 元素和 1 个物理像素相等 canvas.width = dpr * width; canvas.height = dpr * height; // 根据 DPR 设置 Canvas 的宽高属性 ctx.scale(dpr, dpr); renderPath(ctx); }; // 绘制低清晰度画布 const initLDCanvas = () => { const ctx = ldCanvasRef.current.getContext('2d'); renderPath(ctx); }; useEffect(() => { initHDCanvas(); initLDCanvas(); }, []); return ( <> <p>已适配 画布像素:画板像素(物理像素)= 1:1</p> {/* 高清画布 */} <canvas ref={hdCanvasRef} style={{ border: '1px solid #000', width: 200, height: 200 }}></canvas> <br /> <p>未适配 画布像素:画板像素(物理像素)= {window.devicePixelRatio}:1</p> {/* 普通画布 */} <canvas ref={ldCanvasRef} width="200" height="200" style={{ border: '1px solid #000' }}></canvas> </> ); }
Result
Loading...
tip
样式设置的 width
是的元素内容宽度,不包括内边距、边框、外边距的,而 clientWidth
包括内边距,不包括边框、外边距、滚动条的(如果有)。