translate(x, y)
用来移动 canvas
的原点到指定的位置
translate
方法接受两个参数。x
是左右偏移量,y
是上下偏移量,如右图所示。
在做变形之前先保存状态是一个良好的习惯。大多数情况下,调用 restore
方法比手动恢复原先的状态要简单得多。又如果你是在一个循环中做位移但没有保存和恢复canvas
的状态,很可能到最后会发现怎么有些东西不见了,那是因为它很可能已经超出 canvas
范围以外了。
注意:translate
移动的是canvas
的坐标原点。(坐标变换)
1 var ctx;
2 function draw(){
3 var canvas = document.getElementById('tutorial1');
4 if (!canvas.getContext) return;
5 var ctx = canvas.getContext("2d");
6 ctx.save(); //保存坐原点平移之前的状态
7 ctx.translate(100, 100);
8 ctx.strokeRect(0, 0, 100, 100)
9 ctx.restore(); //恢复到最初状态
10 ctx.translate(220, 220);
11 ctx.fillRect(0, 0, 100, 100)
12 }
13 draw();
rotate(angle)
旋转坐标轴。
这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
旋转的中心是坐标原点。
1 var ctx;
2 function draw(){
3 var canvas = document.getElementById('tutorial1');
4 if (!canvas.getContext) return;
5 var ctx = canvas.getContext("2d");
6
7 ctx.fillStyle = "red";
8 ctx.save();
9
10 ctx.translate(100, 100);
11 ctx.rotate(Math.PI / 180 * 45);
12 ctx.fillStyle = "blue";
13 ctx.fillRect(0, 0, 100, 100);
14 ctx.restore();
15
16 ctx.save();
17 ctx.translate(0, 0);
18 ctx.fillRect(0, 0, 50, 50)
19 ctx.restore();
20 }
21 draw();
scale(x, y)
我们用它来增减图形在 canvas
中的像素数目,对形状,位图进行缩小或者放大。
scale
方法接受两个参数。x,y
分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩 小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。
默认情况下,canvas
的 1 单位就是 1 个像素。举例说,如果我们设置缩放因子是 0.5,1 个单位就变成对应 0.5 个像素,这样绘制出来的形状就会是原先的一半。同理,设置为 2.0 时,1 个单位就对应变成了 2 像素,绘制的结果就是图形放大了 2 倍。
transform(a, b, c, d, e, f)
a (m11)
Horizontal scaling.
b (m12)
Horizontal skewing.
c (m21)
Vertical skewing.
d (m22)
Vertical scaling.
e (dx)
Horizontal moving.
f (dy)
Vertical moving.
1 var ctx;
2 function draw(){
3 var canvas = document.getElementById('tutorial1');
4 if (!canvas.getContext) return;
5 var ctx = canvas.getContext("2d");
6 ctx.transform(1, 1, 0, 1, 0, 0);
7 ctx.fillRect(0, 0, 100, 100);
8 }
9 draw();
在前面的所有例子中、,我们总是将一个图形画在另一个之上,对于其他更多的情况,仅仅这样是远远不够的。比如,对合成的图形来说,绘制顺序会有限制。不过,我们可以利用 globalCompositeOperation
属性来改变这种状况。
globalCompositeOperation
= type
1 var ctx;
2 function draw(){
3 var canvas = document.getElementById('tutorial1');
4 if (!canvas.getContext) return;
5 var ctx = canvas.getContext("2d");
6
7 ctx.fillStyle = "blue";
8 ctx.fillRect(0, 0, 200, 200);
9
10 ctx.globalCompositeOperation = "source-over"; //全局合成操作
11 ctx.fillStyle = "red";
12 ctx.fillRect(100, 100, 200, 200);
13 }
14 draw();
15
16 </script>
注:下面的展示中,蓝色是原有的,红色是新的。
type `是下面 13 种字符串值之一:
source-over(default)
这是默认设置,新图像会覆盖在原有图像。
source-in
仅仅会出现新图像与原来图像重叠的部分,其他区域都变成透明的。(包括其他的老图像区域也会透明)
source-out
仅仅显示新图像与老图像没有重叠的部分,其余部分全部透明。(老图像也不显示)
source-atop
新图像仅仅显示与老图像重叠区域。老图像仍然可以显示。
destination-over
新图像会在老图像的下面。
destination-in
仅仅新老图像重叠部分的老图像被显示,其他区域全部透明。
destination-out
仅仅老图像与新图像没有重叠的部分。 注意显示的是老图像的部分区域。
destination-atop
老图像仅仅仅仅显示重叠部分,新图像会显示在老图像的下面。
lighter
新老图像都显示,但是重叠区域的颜色做加处理
darken
保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的)
blue: #0000ff
red: #ff0000
所以重叠部分的颜色:#000000
lighten
保证重叠部分最量的像素。(每个颜色位进行比较,得到最大的)
blue: #0000ff
red: #ff0000
所以重叠部分的颜色:#ff00ff
xor
重叠部分会变成透明
copy
只有新图像会被保留,其余的全部被清除(边透明)
clip()
把已经创建的路径转换成裁剪路径。
裁剪路径的作用是遮罩。只显示裁剪路径内的区域,裁剪路径外的区域会被隐藏。
注意:clip()
只能遮罩在这个方法调用之后绘制的图像,如果是clip()
方法调用之前绘制的图像,则无法实现遮罩。
1 var ctx;
2 function draw(){
3 var canvas = document.getElementById('tutorial1');
4 if (!canvas.getContext) return;
5 var ctx = canvas.getContext("2d");
6
7 ctx.beginPath();
8 ctx.arc(20,20, 100, 0, Math.PI * 2);
9 ctx.clip();
10
11 ctx.fillStyle = "pink";
12 ctx.fillRect(20, 20, 100,100);
13 }
14 draw();
canvas
再绘制每一帧动画之前,需要清空所有。清空所有最简单的做法就是clearRect()
方法
canvas
状态
如果在绘制的过程中会更改canvas
的状态(颜色、移动了坐标原点等),又在绘制每一帧时都是原始状态的话,则最好保存下canvas
的状态
canvas
状态
如果你前面保存了canvas
状态,则应该在绘制完成一帧之后恢复canvas
状态。
我们可用通过canvas
的方法或者自定义的方法把图像会知道到canvas
上。正常情况,我们能看到绘制的结果是在脚本执行结束之后。例如,我们不可能在一个 for
循环内部完成动画。
也就是,为了执行动画,我们需要一些可以定时执行重绘的方法。
一般用到下面三个方法:
setInterval()
setTimeout()
requestAnimationFrame()
1 let sun;
2 let earth;
3 let moon;
4 let ctx;
5 function init(){
6 sun = new Image();
7 earth = new Image();
8 moon = new Image();
9 sun.src = "sun.png";
10 earth.src = "earth.png";
11 moon.src = "moon.png";
12
13 let canvas = document.querySelector("#solar");
14 ctx = canvas.getContext("2d");
15
16 sun.onload = function (){
17 draw()
18 }
19
20 }
21 init();
22 function draw(){
23 ctx.clearRect(0, 0, 300, 300); //清空所有的内容
24 /*绘制 太阳*/
25 ctx.drawImage(sun, 0, 0, 300, 300);
26
27 ctx.save();
28 ctx.translate(150, 150);
29
30 //绘制earth轨道
31 ctx.beginPath();
32 ctx.strokeStyle = "rgba(255,255,0,0.5)";
33 ctx.arc(0, 0, 100, 0, 2 * Math.PI)
34 ctx.stroke()
35
36 let time = new Date();
37 //绘制地球
38 ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds())
39 ctx.translate(100, 0);
40 ctx.drawImage(earth, -12, -12)
41
42 //绘制月球轨道
43 ctx.beginPath();
44 ctx.strokeStyle = "rgba(255,255,255,.3)";
45 ctx.arc(0, 0, 40, 0, 2 * Math.PI);
46 ctx.stroke();
47
48 //绘制月球
49 ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds());
50 ctx.translate(40, 0);
51 ctx.drawImage(moon, -3.5, -3.5);
52 ctx.restore();
53
54 requestAnimationFrame(draw);
55 }
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>Title</title>
6 <style>
7 body {
8 padding: 0;
9 margin: 0;
10 background-color: rgba(0, 0, 0, 0.1)
11 }
12
13 canvas {
14 display: block;
15 margin: 200px auto;
16 }
17 </style>
18 </head>
19 <body>
20 <canvas id="solar" width="300" height="300"></canvas>
21 <script>
22 init();
23
24 function init(){
25 let canvas = document.querySelector("#solar");
26 let ctx = canvas.getContext("2d");
27 draw(ctx);
28 }
29
30 function draw(ctx){
31 requestAnimationFrame(function step(){
32 drawDial(ctx); //绘制表盘
33 drawAllHands(ctx); //绘制时分秒针
34 requestAnimationFrame(step);
35 });
36 }
37 /*绘制时分秒针*/
38 function drawAllHands(ctx){
39 let time = new Date();
40
41 let s = time.getSeconds();
42 let m = time.getMinutes();
43 let h = time.getHours();
44
45 let pi = Math.PI;
46 let secondAngle = pi / 180 * 6 * s; //计算出来s针的弧度
47 let minuteAngle = pi / 180 * 6 * m + secondAngle / 60; //计算出来分针的弧度
48 let hourAngle = pi / 180 * 30 * h + minuteAngle / 12; //计算出来时针的弧度
49
50 drawHand(hourAngle, 60, 6, "red", ctx); //绘制时针
51 drawHand(minuteAngle, 106, 4, "green", ctx); //绘制分针
52 drawHand(secondAngle, 129, 2, "blue", ctx); //绘制秒针
53 }
54 /*绘制时针、或分针、或秒针
55 * 参数1:要绘制的针的角度
56 * 参数2:要绘制的针的长度
57 * 参数3:要绘制的针的宽度
58 * 参数4:要绘制的针的颜色
59 * 参数4:ctx
60 * */
61 function drawHand(angle, len, width, color, ctx){
62 ctx.save();
63 ctx.translate(150, 150); //把坐标轴的远点平移到原来的中心
64 ctx.rotate(-Math.PI / 2 + angle); //旋转坐标轴。 x轴就是针的角度
65 ctx.beginPath();
66 ctx.moveTo(-4, 0);
67 ctx.lineTo(len, 0); // 沿着x轴绘制针
68 ctx.lineWidth = width;
69 ctx.strokeStyle = color;
70 ctx.lineCap = "round";
71 ctx.stroke();
72 ctx.closePath();
73 ctx.restore();
74 }
75
76 /*绘制表盘*/
77 function drawDial(ctx){
78 let pi = Math.PI;
79
80 ctx.clearRect(0, 0, 300, 300); //清除所有内容
81 ctx.save();
82
83 ctx.translate(150, 150); //一定坐标原点到原来的中心
84 ctx.beginPath();
85 ctx.arc(0, 0, 148, 0, 2 * pi); //绘制圆周
86 ctx.stroke();
87 ctx.closePath();
88
89 for (let i = 0; i < 60; i++){//绘制刻度。
90 ctx.save();
91 ctx.rotate(-pi / 2 + i * pi / 30); //旋转坐标轴。坐标轴x的正方形从 向上开始算起
92 ctx.beginPath();
93 ctx.moveTo(110, 0);
94 ctx.lineTo(140, 0);
95 ctx.lineWidth = i % 5 ? 2 : 4;
96 ctx.strokeStyle = i % 5 ? "blue" : "red";
97 ctx.stroke();
98 ctx.closePath();
99 ctx.restore();
100 }
101 ctx.restore();
102 }
103 </script>
104 </body>
105 </html>