html5 canvas

html5里新增了一些标签,为啥单独把canvas拿出来讲呢,因为它可以实现一些很酷的动画,让我们一起从零开始学习吧。

canvas 元素

1
<canvas id="tutorial" width="150" height="150"></canvas>

它只有两个属性,width和height,可选项,默认是300px*150px。

由于有些较老的浏览器不支持canvas,我们会在canvas内部写上你想让不支持的浏览器里显示的内容。如下:

1
2
3
<canvas id="tutorial" width="150" height="150">
current stock price: $3.15 +0.15
</canvas>

注意:结束标签</canvas>不可省略

渲染上下文

<canvas>元素创造了一个画布它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容。这里主要介绍2D图像,通过使用getContext()方法获取渲染上下文和它的绘画功能。

1
2
var canvas = document.getElementById('tutorial');
var ctx = canvas.getContext('2d');

用脚本检测浏览器支持性

1
2
3
4
5
6
7
8
var canvas = document.getElementById('tutorial');

if (canvas.getContext){
var ctx = canvas.getContext('2d');
// drawing code here
} else {
// canvas-unsupported code here
}

栅格

canvas画布的起点是网格的左上角,坐标(x,y),分别表示距离y轴和x轴的距离,如下图所示。

Canvas_default_grid

绘制矩形

  • fillRect(x, y, width, height):绘制一个填充的矩形
  • strokeRect(x, y, width, height):绘制一个矩形的边框
  • clearRect(x, y, width, height):清除指定矩形区域,让清除部分完全透明

    参数:
    x:矩形起始点的 x 轴坐标。
    y:矩形起始点的 y 轴坐标。
    width:矩形的宽度。
    height:矩形的高度。

示例
1
<canvas id="canvas" width="300" height="300">support</canvas>
1
2
3
4
5
6
7
8
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.fillStyle = "blue";
ctx.fillRect(30, 30, 100, 100);
ctx.clearRect(50, 50, 60, 60);
ctx.strokeRect(60, 60, 40, 40);
}

canvas-1

绘制路径

  • beginPath():新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径
  • closePath():闭合路径之后图形绘制命令又重新指向到上下文中(不是必须的)
  • stroke():通过线条来绘制图形轮廓
  • fill():通过填充路径的内容区域生成实心的图形

注意:当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。

  • moveTo(x, y):笔触,一个画笔在一个画布的哪个位置落笔。
  • lineTo(x, y):笔触后,定义停止的位置。
    参数:
    x:x 轴坐标。
    y:y 轴坐标。
  • arc(x, y, radius, startAngle, endAngle, anticlockwise):绘制圆或圆弧之类的路径
    参数:
    x:圆心位置 x 轴坐标。
    y:圆心位置 y 轴坐标。
    radius:圆的半径。
    startAngle:起始角度。
    endAngle:终点角度。
    anticlockwise:可选项,默认是顺时针,如果填写true,则为逆时针。
  • arcTo((x1, y1, x2, y2, radius):根据控制点和半径绘制圆弧路径的方法,使用直线连接前一个点。
    参数:
    x1:第一个控制点的 x 轴坐标。
    y1:第一个控制点的 y 轴坐标。
    x2:第二个控制点的 x 轴坐标。
    y2:第二个控制点的 y 轴坐标。
    radius:半径。
    注意:两个控制点连接的线及延长线与所画的圆相切
  • quadraticCurveTo(cp1x, cp1y, x, y):二次贝塞尔曲线
    参数:
    cp1x:控制点的 x 轴坐标。
    cp1y:控制点的 y 轴坐标。
    x:终点的 x 轴坐标。
    y:终点的 y 轴坐标。
    注意:控制点与起点和终点相连的两条线及延长线与所画的圆相切
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y):三次贝塞尔曲线
    参数:
    cp1x:第一个控制点的 x 轴坐标。
    cp1y:第一个控制点的 y 轴坐标。
    cp2x:第二个控制点的 x 轴坐标。
    cp2y:第二个控制点的 y 轴坐标。
    x:终点的 x 轴坐标。
    y:终点的 y 轴坐标。
    注意:第一个控制点与起点相连,第二个控制点与终点相连的两条线及延长线与所画的圆相切

色彩

  • fillStyle = color:设置图形的填充颜色。
  • strokeStyle = color:设置图形轮廓颜色。
示例
1
<canvas id="canvas" width="300" height="300">support</canvas>
1
2
3
4
5
6
7
8
9
10
11
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
for (var i=0;i<6;i++){
for (var j=0;j<6;j++){
ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' +
Math.floor(42.5*j) + ',0)';
ctx.fillRect(j*25,i*25,25,25);
}
}
}

canvas-2

1
<canvas id="canvas" width="300" height="300">support</canvas>
1
2
3
4
5
6
7
8
9
10
11
12
13
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
for (var i=0;i<6;i++){
for (var j=0;j<6;j++){
ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' +
Math.floor(255-42.5*j) + ')';
ctx.beginPath();
ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);
ctx.stroke();
}
}
}

canvas-3

颜色渐变

  • createLinearGradient(x0, y0, x1, y1):方法创建一个沿参数坐标指定的直线的渐变
    参数:
    x0:起点的 x 轴坐标。
    y0:起点的 y 轴坐标。
    x1:终点的 x 轴坐标。
    y1:终点的 y 轴坐标。
  • createRadialGradient(x0, y0, r0, x1, y1, r1):两个圆的坐标,绘制放射性渐变的方法。
    参数:
    x0:开始圆形的 x 轴坐标。
    y0:开始圆形的 y 轴坐标。
    r0:开始圆形的半径。
    x1:结束圆形的 x 轴坐标。
    y1:结束圆形的 y 轴坐标。
    r1:结束圆形的半径。

绘制文本

  • fillText(text, x, y [, maxWidth]):在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的。
  • strokeText(text, x, y [, maxWidth]):在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的。
案例
1
<canvas id="canvas" width="300" height="300">support</canvas>
1
2
3
4
5
6
7
8
9
10
11
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
ctx.shadowBlur = 8;
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
ctx.font = "48px serif";
ctx.fillText("Hello world", 10, 50);
ctx.strokeText("Hello world", 10, 90);
}

canvas-4

动画

你需要进行以下一些步骤来画出一帧:
1.清空 canvas
除非接下来要画的内容会完全充满 canvas (例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。
2.保存 canvas 状态
如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
3.绘制动画图形(animated shapes)
这一步才是重绘动画帧。
4.恢复 canvas 状态
如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。

案例
1
<canvas id="canvas" width="300" height="300">support</canvas>
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
function clock(){
var now = new Date();
var ctx = document.getElementById('canvas').getContext('2d');
ctx.save();
ctx.clearRect(0,0,150,150);
ctx.translate(75,75);
ctx.scale(0.4,0.4);
ctx.rotate(-Math.PI/2);
ctx.strokeStyle = "black";
ctx.fillStyle = "white";
ctx.lineWidth = 8;
ctx.lineCap = "round";

// Hour marks
ctx.save();
for (var i=0;i<12;i++){
ctx.beginPath();
ctx.rotate(Math.PI/6);
ctx.moveTo(100,0);
ctx.lineTo(120,0);
ctx.stroke();
}
ctx.restore();

// Minute marks
ctx.save();
ctx.lineWidth = 5;
for (i=0;i<60;i++){
if (i%5!=0) {
ctx.beginPath();
ctx.moveTo(117,0);
ctx.lineTo(120,0);
ctx.stroke();
}
ctx.rotate(Math.PI/30);
}
ctx.restore();

var sec = now.getSeconds();
var min = now.getMinutes();
var hr = now.getHours();
hr = hr>=12 ? hr-12 : hr;

ctx.fillStyle = "black";

// write Hours
ctx.save();
ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec )
ctx.lineWidth = 14;
ctx.beginPath();
ctx.moveTo(-20,0);
ctx.lineTo(80,0);
ctx.stroke();
ctx.restore();

// write Minutes
ctx.save();
ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec )
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(-28,0);
ctx.lineTo(112,0);
ctx.stroke();
ctx.restore();

// Write seconds
ctx.save();
ctx.rotate(sec * Math.PI/30);
ctx.strokeStyle = "#D40000";
ctx.fillStyle = "#D40000";
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(-30,0);
ctx.lineTo(83,0);
ctx.stroke();
ctx.beginPath();
ctx.arc(0,0,10,0,Math.PI*2,true);
ctx.fill();
ctx.beginPath();
ctx.arc(95,0,10,0,Math.PI*2,true);
ctx.stroke();
ctx.fillStyle = "rgba(0,0,0,0)";
ctx.arc(0,0,3,0,Math.PI*2,true);
ctx.fill();
ctx.restore();

ctx.beginPath();
ctx.lineWidth = 14;
ctx.strokeStyle = '#325FA2';
ctx.arc(0,0,142,0,Math.PI*2,true);
ctx.stroke();

ctx.restore();

window.requestAnimationFrame(clock);
}

window.requestAnimationFrame(clock);

support

坚持原创技术分享,您的支持将鼓励我继续创作!