H5之canvas标签(一)

HTML5中添加了很多新的标签,canvas就是其中之一,它被用来进行图形的绘制,关于图形的绘制我们需要通过javascript来完成,canvas标签仅是一个图形的容器。

IE9及其他现代浏览器基本都支持这个标签。

canvas由几组API组成,除了绘制基本图形的2D上下文,还有一个名为WebGL的3D上下文,不过浏览器支持还不够好,

基本用法

使用\时,我们需要设置其宽高,用来指定绘图的区域。出现在开始和结束标签之间的内容作为后备信息,当浏览器不支持该标签时,里面的内容就会显示出来,HTML5中其他标签也是这么干的。
一个简单的canvas实例如下:

1
<canvas width="299" height="199" id="myCanvas">sorry,the canvas isn't supported.</canvas>

这是我们的画布,也是我们绘制的图像的一个载体,要在画布上绘图,我们需要先通过getContext()方法取得绘图上下文:

1
2
3
4
5
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
}

使用toDataUrl()方法可以导出在canvas上绘制的图像。该方法接受一个MIME类型参数,如果我们需要取得画布中的一幅png图像,可以这样做:

1
2
3
4
5
6
7
8
var drawing = document.getElementById("myCanvas");
if(drawing.getContext){
var imageUrl = drawing.toDataUrl("image/png");
//显示图像
var image = document.createElement("img");
image.src = imageUrl;
document.body.append(image);
}

默认情况下,MIME类型为PNG格式。

canvas坐标系

canvas以左上角为原点(0,0),坐标值都以原点为参照进行计算,与background背景图片的位置计算方法相同。第一个值代表离左边框的距离,第二个值代表离上边框的距离。

填充和描边

通过2D绘图上下文提供的方法,我们可以绘制矩形,弧线等2D图形。对于这些图形,我们可以选择绘制的方式。这里有两个属性,来决定是进行填充还是进行描边:fillStyle和strokeStyle

这两个属性的值可以是字符串、渐变对象、或模式对象,默认值都是“#000000”。我们可以用任何的颜色格式(rgb、rgba、hsl、hsla)来定义样式:

1
2
3
4
5
6
7
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
context.strokeStyle = "green";
context.fillStyle = "#f6c";
}

这样设置后,下面涉及到填充和描边的操作都会使用这两个样式,绘制过程中样式可更改。

绘制矩形

与绘制矩形相关的方法有:fillRect()、strokeRect()、clearRect(),这些参数都接受4个参数:矩形x坐标、矩形y坐标、矩形宽度、矩形高度,单位都为像素。

绘制图形前都应先指定填充或者描边的样式:

1
2
3
4
5
6
7
8
9
10
11
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
context.strokeStyle = "green";
context.strokeRect(10,10,50,50);
context.fillStyle = "rgba(0,0,0,0.6)";
context.fillRect(100,20,30,30);
}

此外,还有以下属性能够控制线条和线条末端的形状:

  • lineWidth:线条宽度
  • lineCap:用于控制线条末端的形状,取值有butt(平头),round(圆头),square(方头)
  • lineJoin:控制线条相交的方式,取值有round(圆交),bevel(斜交),miter(斜接)

绘制路径

2D绘图上下文提供了很多用于绘制路径的方法。在绘制之前,需要先调用beginPath(),表示要开始绘制新路径。然后再调用下面的方法绘制实际路径。

绘制圆弧

arc(x,y,radius,startAngle,endAngle,conterclockwise):以(x,y)为圆心,radius为半径,起始和结束弧度分别为startAngle,endAngle来画圆弧,counterclockwise为false代表按逆时针计算角度。

我们用这个方法来绘制一个圆形:

1
2
3
4
5
6
7
8
9
10
11
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
context.beginPath();
context.arc(100,100,40,0,Math.PI*2,true)'
//注意,这里不关闭路径的话会从上个路径继续绘制下个图形
context.closePath();
context.fillStyle = 'rgba(0,0,0,0.6)';
context.fill();
}

canvas1

绘制完一个图形要关闭路径,并且最后需要调用fill()和stroke()才能将图形绘制到画布上。

画圆弧还有另外一个方法,arcTo(x1,y1,x2,y2,radius):传入弧起点和终点的坐标以及弧的半径。

moveTo()和lineTo()

  • moveTo(x,y):将绘图游标移动到(x,y),不画线
  • lineTo(x,y):在上一点和(x,y)之间绘制一条直线

注意:

每次画线都是从moveTo的点到lineTo的点

如果没有moveTo,第一次lineTo的效果和moveTo相同

每次lineTo后如果没有moveTo,下次会从上次lineTo的终点开始画线

结合上面和圆弧来写一个时钟表盘的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
context.beginPath();
//绘制外圆
context.arc(100,100,80,0,Math.PI*2,false);
//绘制内圆
context.arc(100,100,75,0,Math.PI*2,false);
//绘制分针
context.moveTo(100,100);
context.lineTo(100,35);
//绘制时针
context.moveTo(100,100);
context.lineTo(47,100);
//描边
context.stroke();
}

效果:

cavas2

贝塞尔曲线(bezier)

我们可以通过bezierCurveTo()方法来画贝塞尔曲线,该方法有6个参数:clx,cly,c2x,c2y,x,y。该方法会从上一点到(x,y)之间绘制一条曲线,并以(c1x,c1y)和(c2x,c2y)为控制点。

还有一个跟这个方法类似的方法–quadraticCurveTo(c1x,c2y,x,y),用法相同,区别在于只有一个控制点(c1x,c1y),这是一个二次曲线的绘制方法。

如下例:

1
2
3
4
5
6
7
8
9
10
11
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
context.beginPath();
context.moveTo(50,50);
context.bezierCurveTo(50, 50,150, 50, 150, 100);
context.quadraticCurveTo(150, 150, 250, 200);
context.stroke();
}

效果:

canvas3

绘制文本

绘制文本主要有两个方法:fillText()和strokeText()。这两个方法都接收4个参数:要绘制的字符串,x坐标,y坐标,最大像素宽度。

同时,这两个方法都以下面3个属性为基础:

  • font:表示文本样式、大小和字体,如”bold 14px Arial“
  • textAlign:表示文本对齐方式,取值有start、end、left、right、center。建议使用start和end,而不是left、right
  • textBaseline:表示文本的基线(垂直对齐方式),取值有top、hanging、middle、alphabetic、ideographic、bottom

这几个属性值都有默认值,fillText()会使用fillStyle来填充文字,strokeText()则使用strokeStyle为文字描边。

绘制文本比较复杂,特别是但我们需要将文本控制在某个区域中时,为此,2d上下文为我们提供了一个确定文本你大小的方法measureText(),返回的是个包含width属性的对象。

现在,如果我们想在一个150px宽度的矩形中绘制“hello canvas!”,我们可以这样做:

1
2
3
4
5
6
7
8
var fontSize = 30
context.font = fontSize+"px Arial";
while(context.measureText("hello canvas!")>150){
fontSize--;
context.font = fontSize + "px Arial";
}
context.fillText("hello canvas!",10,10);

上面代码会从30像素开始递减,直到文本的宽度小于150px,即找到合适的字体大小。

渐变

canvas中的渐变我们通过canvasGradient实例来实现,生成渐变对象的方法有两个:

  • createLinearGradient(x1,y1,x2,y2):接收渐变的起点(x1,y1)和终点和(x2,y2)
  • createRadialGradient(x1,y1,r1,x2,y2,r2):跟上面方法类似,前三个参数定义一个以(x1,y1)为圆心,r1为半径的圆,后三个参数定义一个以(x2,y2)为圆心,r2为半径的圆

通过上述方法创建好实例后,需要用addColorStop方法来上色,
addColorStop()有两个参数:色标位置以及css颜色值,色标位置介于0到1之间。

可以根据需要添加多个色标。

来看2个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
//创建一个线性渐变实例
var gradient = context.createLinearGradient(50,50,150,150);
gradient.addColorStop(0,'#33cccc');
gradient.addColorStop(1,'#ffccff');
context.fillStyle = gradient;
context.fillRect(50,50,100,100);
//创建径向渐变实例
var radial = context.createRadialGradient(250,100,20,300,150,50);
radial.addColorStop(0,'white');
radial.addColorStop(1****,'black');
context.fillRect(200,50,100,100);
}

效果:

canvas6

阴影

2D上下文提供了几个绘制阴影的属性,这些属性会自动为图形或者路径添加阴影。

  • shadowColor:css颜色值形式的阴影颜色
  • shadowOffsetX:沿x轴的偏移量
  • shadowOffsetY:沿y轴的偏移量
  • shadowBlur:模糊的像素数,默认为0

看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
//创建一个线性渐变实例
context.shadowOffsetX = 5;
context.shadowOffsetY = 4;
context.shadowBlur = 5;
context.shadowColor = '#cff8de';
context.fillStyle="#3399ff";
context.fillRect(40,40,100,100);
}

效果:

canvas7

变形

2D绘制上下文支持我们对图像进行绘制变换,变换时我们需要用到变换矩阵,下面的方法会改变变换矩阵,从而导致不同的效果。

save()和restore()

了解变形之前需要先知道两个在绘制图形时必不可少的方法–save()和restore(),他们是用来保存和回复canvas状态的,不需要传入参数。

canvas状态存储在栈中,当save()方法被调用时,当前的状态就会被保存到栈中。

canvas状态包括的内容有:

  • 当前应用的变形(scale,translate,rotate)
  • strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值
  • 当前的裁剪路径(clipping path)

而restore()方法被调用时,则会从栈中弹出上一个保存的状态,恢复所有设定。

translate()

translate(x,y)方法接收新位置的坐标,x是左右偏移量,y是上下偏移量,表示将坐标原点移到(x,y)。

这里需要牢记偏移的是坐标原点,实际上所有变形操作的都是坐标原点。

在变形之前用save()保存状态是一个好的习惯,因为在一个循环中做位移但没有保存和回复canvas状态,最后有些东西会不见,因为它很可能超出了canvas范围之外了。

rotate()

rotate()方法接收一个angle参数,通过这个方法我们可以围绕原点旋转图像。

通过旋转,我们可以绘制有趣的图形:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
context.translate(100,100);
context.translate(100,100);
for(var i=1;i<6;i++){
context.save();
context.fillStyle='rgb('+(51*i)+',255,'+(255-51*i)+')';
for(var j=0;j<i*6;j++){
context.rotate(Math.PI*2/(i*6));
context.beginPath();
context.arc(0,i*12.5,5,0,Math.PI*2,true);
context.fill();
}
context.restore();
}
}

效果:

canvas4

scale()

变形的另外一个方法是scale(x,y),即缩放。它接收两个参数,分别为x轴和y轴的缩放因子,默认都是1.

transform

变形的最后一个方法是transform(m11,m12,m21,m22,dx,dy),这个方法会讲当前的变形矩阵乘上一个基于自身参数的矩阵,各个参数的意义如下:

  • m11:水平方向的缩放
  • m12:水平方向的偏移
  • m21:垂直方向的偏移
  • m22:垂直方向的缩放
  • dx:水平方向的移动
  • dy:垂直方向的移动

setTransform(m11,m12,m21,m22,dx,dy)会将变化矩阵重置为单位矩阵然后再调用transform()。

resetTransform(m11,m12,m21,m22,dx,dy)将当前变形为单位矩阵。

tranlate()、scale()、rotate()三个方法调用的先后顺序不同,最后出现的效果也不同,但是只要记住这三个方法都是操作的坐标轴,就会好理解得多了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
var drawing = document.getElementById("myCanvas");
//检测浏览器是否支持canvas
if(drawing.getContext){
var context = drawing.getContext("2d");
context.save(); //保存了当前context的状态
context.fillStyle = "#EEEEFF";
context.fillRect(0, 0, 400, 300);
context.fillStyle = "red";
//平移 缩放 旋转
context.translate(100, 100);
context.scale(0.5, 0.5);
context.rotate(Math.PI / 4);
context.fillRect(0, 0, 100, 100);
context.restore(); //恢复到刚刚保存的状态
context.save(); //保存了当前context的状态
context.fillStyle = "green";
//缩放 平移 旋转 2 1 3
context.scale(0.5, 0.5);
context.translate(100, 100);
context.rotate(Math.PI / 4);
context.fillRect(0, 0, 100, 100);
context.restore(); //恢复到刚刚保存的状态
context.save(); //保存了当前context的状态
context.fillStyle = "blue";
//缩放 旋转 平移 2 3 1
context.scale(0.5, 0.5);
context.rotate(Math.PI / 4);
context.translate(100, 100);
context.fillRect(0, 0, 100, 100);
context.restore(); //恢复到刚刚保存的状态
context.save(); //保存了当前context的状态
context.fillStyle = "pink";
//旋转 平移 缩放 3 1 2
context.rotate(Math.PI / 4);
context.translate(100, 100);
context.scale(0.5, 0.5);
context.fillRect(0, 0, 100, 100);
}

效果:

canvas5

(平移,缩放,旋转)和(平移,旋转,缩放)效果一样,(缩放,旋转,平移)和(旋转,缩放,平移)一样