大家好,我是前端西瓜哥。今天简单说说 Canvas 入门的基础知识。
Canvas 是以 <Cavnas>
作为显示载体,并通过 JavaScript 脚本来绘制位图的技术。
canvas 元素
canvas,画布,这个名字就非常形象,是我们绘制图像的地方。对应的 DOM 元素就是 <canvas>
。
我们通过 width 和 height 来设置 canvas 元素的宽高。
<canvas width="500" height="500"></canvas>
width 和 height 只支持数字,单位为 CSS 像素值(px),默认值分别为 300 和 150。
如果你提供的是 "100%",它会转换为 100;如果你提供的是非数字,结果就是设置失败,然后拿到默认值。
你可以认为 canvas 获取宽高时进行了类似 parseInt
的处理。
另外,如果你手动修改了 canvas 的宽或高,canvas 的内容会全部清空。
需要特别注意的是,canvas 的宽高不能通过 style 属性来设置。style.width
和 style.height
其实是在设置了宽高的 canvas 上再进行了缩放。某种意义上,你可以把 canvas 元素当作 img 元素。
canvas 的宽高并不是可以设置为无限大的,不同的浏览器允许的最大宽高不同。一旦超过,就会啪地一下变成了 0。一般来说我们没有设置很大的宽高的需求,通常最大为显示屏的宽高就差不多了。
渲染上下文
const canvas = document.querySelector('canvas'); const ctx = canvas.getContext('2d');
在绘制图形之前,我们需要做一些准备工作,通过 canvas 元素调用 getContext,拿到一个渲染上下文 context。
就好比是拿到只能在特定画布上的画布的画笔。
坐标系
对于计算机来说,坐标系的原点位于屏幕的左上角,向下为 y 轴正方向,向右为 x 轴正方向。
这点和我们学数学坐标系有点不同,要注意一下。
下面我们来看看怎么绘制各种图形。
画个矩形
ctx.fillStyle = '#f04'; // 红色 ctx.fillRect(10 , 10, 200, 100); // 矩形(填充) ctx.strokeStyle = 'black'; ctx.lineWidth = 4; ctx.strokeRect(10, 10, 200, 100); // 矩形(描边)
这两个方法都要传入矩形的左上角位置 (10, 10)
和宽高(200 和 100)。
fillRect 用于填充矩形区域,strokeRect 则是描边。
fillStyle 用于设置填充的颜色,strokeStyle 用于设置描边的颜色,lineWidth 只用描边的线宽。
绘制的结果如下:
可以类比给 Div 元素设置 border 和 background。
Dom 和 Canvas 两种渲染方式的对比
我们不妨将 Cavans 和 Div 展现一个矩形的绘制过程进行对比。
首先,Div 是声明式的,它保留在 Dom 树下,随时可以修改属性,修改后渲染引擎对其进行重新绘制。
而 Canvas 绘制矩形则是命令式的,一旦它被画到画布上,Canvas 就不再管这是不是矩形了,Canvas 只是根据命令画出了一系列像素点,自身并不维护一个被绘制图形形成的树。
如果你要记录曾经画的图形,以及形成的层级,都要开发者自己去维护。
相比 Dom 自动地维护,隐形造成的非必要的回流重绘消耗性能的操作,Canvas 会更原始,把决定是否重绘还是局部冲毁堤岸的权利让渡给开发者,让开发者做性能优化有更大的空间。
但这也让开发者需要做更多的底层基础工作。
绘制圆形
Canvas 并没有提供直接绘制一个圆形的 API,而是提供一个绘制圆弧的 API。
ctx.fillStyle = '#519D36'; // 绿色 ctx.beginPath(); ctx.arc(100, 40, 20, 0, Math.PI * 2); // 0 为从右方为起点, ctx.fill(); ctx.stroke();
绘制图形通常需要先通过各种 API 描述的子路径组合出一个路径,然后再进行 fill(填充) 和 stroke(描边)。arc 方法就是其中一种描述子路径的 API。
arc 接受的参数依次为:
•圆心位置 (100, 40)
;•半径(20);•起始的弧度(0,对应圆最右侧的点)•结束的弧度(Math.PI * 2
,刚好一圈)•是否为逆时针绘制的布尔值,是可选值。默认为 false,即顺时针。
arc 方法执行完后,Canvas 心里就记住了这个路径。然后调用 fill 方法将路径填充,以及 stroke 方法沿着路径绘制线条。
beginPath 是表示我们后面要绘制新的路径的意思,可以防止描边时和上一次的路径相连。就好比把画笔提起来,准备画下一个图形。
当然我们画圆之前没有画过其他路径,所以这里不执行 beginPath 也是可以的。
绘制的结果如下:
绘制三角形
圆弧会画了,我们再来看看线条怎么画。以画三角形为例:
ctx.moveTo(20, 20); ctx.lineTo(60, 20); ctx.lineTo(40, 40); ctx.fill(); ctx.closePath(); ctx.stroke();
首先是 moveTo 到坐标 (20, 20)
。
moveTo 其实就是将画笔抬起,放到对应的位置。它可以做到 beginPath 的效果:摆脱和之前的路径的关系。
然后是 lineTo 从当前位置沿着直线到达下一个点。接着又来一个 lineTo,此时我们画出了三角形的两条边。
此时如果你 fill(填充)其实是可以画出一个三角形的,因为填充逻辑是会将路径用直线封闭然后在填充的,第三条边存不存在效果相同。
但对 stroke(描边)就不同了,就导致只画出两条边。
于是我们调用 closePath 方法,将当前点和刚刚起笔的点(moveTo 时的点)进行直线相连。其实你可以再调用用一次 moveTo(20, 20)
来替代这个方法,这两个方法是等效的。
绘制的效果为:
结尾
本文介绍了 Canvas 的一些基础知识,并演示了绘制矩形、圆形、三角形比较基础的操作。
晚点我会再写其他介绍其他基础绘制操作的文章。