走进canvas
canvas是什么?
<canvas>是一个可以使用脚本在其中绘制图形的HTML 元素。什么意思呢?就是canvas元素本身并不绘制图形,它只是相当于一张空画布。如果想在canvas上绘制图形,则必须使用 JavaScript脚本来进行绘制。
canvas的基本用法
定义 canvas 元素
1 | <canvas id="canvas" width="1920" height="1200"></canvas> |
<canvas>标签只有两个属性—— width和height,这两个属性都是可选的,当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。要注意一点,因为<canvas>是双标签,所以结束标签</canvas>一定不可以省略。
获取canvas 元素对应的 DOM 对象
1 | //得到DOM对象 |
调用 canvas 对象的 getContext()方法
这个方法是用来获得渲染上下文和绘画功能。该方法会返回一个 canvasRenderingContext2D对象,该对象可以绘制图形。
1 | var ctx = canvas.getContext('2d'); |
调用canvas的方法进行绘图
这些后面会提到。
绘制形状
绘制矩形
- rect(x, y, width, height)
绘制一个左上角坐标为(x,y),宽高为width以及height的矩形。没有填充的话,不会显示。
- fillRect(x, y, width, height)
绘制一个左上角坐标为(x,y),宽高为width以及height并填充的矩形,默认填充颜色为黑色。
- strokeRect(x, y, width, height)
绘制一个左上角坐标为(x,y),宽高为width以及height的矩形边框,默认边框颜色为黑色。
- clearRect(x, y, width, height)
清除指定矩形区域,让清除部分完全透明。1
2
3
4
5
6
7
8//边长为100px的黑色正方形
ctx.fillRect(25,25,100,100);
//从正方形的中心开始擦除了一个60*60px的正方形
ctx.clearRect(45,45,60,60);
//在清除区域内生成一个50*50的正方形边框
ctx.strokeRect(50,50,50,50);
绘制路径
图形的基本元素是路径。一个路径,甚至一个子路径,都是闭合的。以下为常用的几个方法,很多图形其实是通过绘制路径而绘制出来的。
- beginPath()
新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。 - closePath()
闭合路径之后图形绘制命令又重新指向到上下文中。闭合路径closePath(),不是必需的,比如路径使用填充(filled)时,路径会自动闭合,可以不用closePath();但是如果使用描边(stroked)则不会闭合路径。 - stroke()
通过线条来绘制图形轮廓。 - fill()
通过填充路径的内容区域生成实心的图形。 - moveTo(x, y)
把 canvas 的当前路径的结束点移动到 x, y 对应的点。通常会使用moveTo()函数设置起点,也可以绘制一些不连续的路径。
绘制直线
**lineTo(x, y)**:绘制一条从当前位置到指定x以及y位置的直线。
1 | ctx.beginPath(); |
绘制三角形
绘制三角形其实就是绘制好闭合路径,然后对其进行填充或者描边的效果。
1 | // 填充三角形 |
绘制圆弧
- arc(x, y, radius, startAngle, endAngle, anticlockwise)
画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为false,顺时针)来生成。 - arcTo(x1, y1, x2, y2, radius)
根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点(x1, y1)和(x2, y2)。1
2
3
4
5var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(75, 75, 50, 0, 2 * Math.PI);
ctx.stroke();绘制贝塞尔曲线
- quadraticCurveTo(cp1x, cp1y, x, y)
绘制贝塞尔曲线,cp1x,cp1y为控制点,x,y为结束点。 - bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制二次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//绘制一个对话气泡
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(75,25);
ctx.quadraticCurveTo(25,25,25,62.5);
ctx.quadraticCurveTo(25,100,50,100);
ctx.quadraticCurveTo(50,120,30,125);
ctx.quadraticCurveTo(60,120,65,100);
ctx.quadraticCurveTo(125,100,125,62.5);
ctx.quadraticCurveTo(125,25,75,25);
ctx.stroke();
//绘制一颗爱心
ctx.beginPath();
ctx.moveTo(75,40);
ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5);
ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40);
ctx.fill();
添加颜色
- fillStyle = color:设置图形的填充颜色。
- strokeStyle = color:设置图形轮廓的颜色。
1
2
3
4ctx.fillStyle = "blue";
ctx.fillRect(10, 10, 100, 100);
ctx.strokeStyle = "red";
ctx.strokeRect(10, 10, 100, 100);
绘制文本
- fillText(text, x, y [, maxWidth])
在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的。 - strokeText(text, x, y [, maxWidth])
在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的。1
2
3
4//文字是填充的
ctx.fillText("Hello world", 10, 50);
//文字是边框的
ctx.strokeText("Hello world", 10, 50);
使用图片
drawImage(image, x, y)
其中 image 是 image 或者 canvas 对象,x和y是其在目标canvas里的起始坐标。
1 | var img = new Image(); // 创建一个img元素 |
若调用 drawImage 方法时,图片还没装载完,那么什么都不会发生。所以应该用load时间来保证不会在加载完毕之前使用这个图片。
状态的保存和恢复
save()**和restore()方法是用来保存和恢复 canvas 状态的,都没有参数,这两个是相互匹配出现的。当save()方法被调用后,当前的状态就会被推送到栈中保存;当 restore 方法被调用后,上一个保存的状态就从栈中弹出,所有设定都会恢复。那么为什么需要保存canvas状态呢**?
当我们对画布进行旋转,缩放,平移等操作的时候,我们只想对特定的元素进行操作,比如图片,一个矩形等,但是当你用canvas的方法来进行这些操作的时候,其实是对整个画布进行了操作,那么之后在画布上的元素都会受到影响,所以在操作之前调用canvas.save()来保存画布当前的状态,当操作之后取出之前保存过的状态,这样就不会对其他的元素进行影响。
1 | ctx.save(); // 保存默认的状态 |
变形
移动
translate(x, y)
translate方法接受两个参数。x 是左右偏移量,y 是上下偏移量。对于这个方法,很多人会有个误解,认为translate(x,y)是把点(x,y)作为新的坐标原点。其实并不是这样的。Translate(x,y)表示原来的原点分别在x轴和y轴偏移多远的距离,然后以偏移后的位置作为坐标原点。假如原点落在(1,1),那么translate(10,10)就是在原点(1,1)基础上分别在x轴、y轴移动10,则原点变为(11,11)。
旋转
rotate(angle)
这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。要注意的是这里指的是弧度,弧度和度的概念是不一样的,它们之间的关系可以用一个计算公式来表示,弧度=度 × π/180。
缩放
scale(x, y)
scale 方法接受两个参数。x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。比如设置缩放因子是 0.5,1 个单位就变成对应 0.5 个像素,这样绘制出来的形状就会是原先的一半。
动画
画一帧动画的步骤:
(1)清空 canvas
除非接下来要画的内容会完全充满 canvas(如背景图),否则需要清空所有,可以用 clearRect()方法来清空。
(2)保存 canvas 状态
如果要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,需要先保存一下。
(3)绘制动画图形
这一步才是重绘动画帧。
(4)恢复 canvas 状态
如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。
1 | var car = new Image(); |
requestAnimationFrame函数,传递一个callback参数,在下一个动画帧时,会调用callback。这个函数可以告诉浏览器希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。
使用多层画布
在画一个复杂的场景时,有些元素不断地改变或者移动,而其它的元素,如背景,永远不变。这个时候可以考虑用多个画布元素去创建不同层次来进行优化,将需要一直变动的元素和不变的元素分开放在不同层次的画布中,便于操作。这里要注意的是canvas不能嵌套,只能层叠。
多层的canvas可以通过设置定位来实现每一层都可独立移动而相互之间不影响。
1 | //使用3个canvas |