本章主要知识点:
- save() restore() 保存和恢复状态
- translate(x, y), rotate(deg), scale(sx, sy)图形变换
- transform(a, b, c, d, e, f), setTransform(a, b, c, d, e, f) 二维矩阵
图形变换一般指translate, rotate, scale, transform等方法,对图形进行控制。
通常绘制图形一般会采用先绘制基本图形,然后将基本图形进行图形变换得到自己想要的效果。
1.重构
上一章中讲到绘制星星的图形效果(星空效果),我们可以将其重构为,先绘制基本轮廓,然后再进行图形变换操作。
重构为:
// 绘制一个标准的星星
// 小圆半径是大圆的1/2
function starPath(ctx) {
ctx.beginPath();
for (var i = 0; i < 5; i++) {
ctx.lineTo(
Math.cos((18 + i * 72 - rotation) / 180 * Math.PI),
-Math.sin((18 + i * 72 - rotation) / 180 * Math.PI)
);
ctx.lineTo(
Math.cos((54 + i * 72 - rotation) / 180 * Math.PI) * 0.5,
-Math.sin((54 + i * 72 - rotation) / 180 * Math.PI) * 0.5
);
}
ctx.closePath();
}
function drawStar(ctx, x, y, R, rotation) {
// 图形变换操作
// 绘制基本轮廓
starPath(ctx);
}
2.translate, rotate, scale
- translate(x, y): 移动图形
- rotate(deg): 旋转多大弧度
- scale(sx, sy): 缩放,要注意这个函数会产生副作用, 比如比例的平移,或增加描边的宽度等,使用时需要注意
值得注意的是: 图形变换是叠加的,比如:
// 假设图形原坐标是(0, 0)
ctx.translate(100, 100); // 移动到(100, 100)
ctx.fillRect(0, 0, 400, 400); // 绘制一个矩形
// 再次移动
ctx.translate(200, 200);
# 此时的图形坐标变为(100+200, 100+200) => (300, 300)
ctx.fillRect(0, 0, 400, 400)
一般我们移动绘制之后,再将其坐标移回去:
ctx.translate(100, 100); // 移动到(100, 100)
ctx.fillRect(0, 0, 400, 400); // 绘制一个矩形
# 绘制之后移动回去
ctx.translate(-100, -100);
// 再次移动
ctx.translate(200, 200);
# 因为上面进行了还原,此时的图形坐标为(200, 200)
ctx.fillRect(0, 0, 400, 400)
但是这样做显得很繁琐,因此canvas有保存状态,再恢复状态的函数save(), restore()
3.save() | restore()
一般这2个函数成对出现
上面的例子可以写为:
# 先保存之前的状态
ctx.save();
ctx.translate(100, 100); // (100, 100)
ctx.fillRect(0, 0, 400, 400);
# 恢复到保存的状态
ctx.restore();
ctx.save();
ctx.translate(200, 200);
ctx.fillRect(0, 0, 400, 400);
ctx.restore()
4.完成星空
可以在1.重构 的基础上利用图形变换完成星空图:
// 获取随机颜色
function getRandomColor() {
return '#' + ( '00000' + (Math.random() * 0x10000000<<2) ).toString(16).slice(-6);
}
function drawStar(ctx, x, y, R, rotation) {
ctx.save();
// 进行图形变换
ctx.translate(x, y);
ctx.rotate(rotation / 180 * Math.PI);
ctx.scale(R, R);
// 然后绘制基本的轮廓
starPath(ctx);
ctx.fillStyle = getRandomColor();
ctx.fill();
ctx.restore();
}
for (var i = 0; i < 100; i++) {
var ran = Math.random();
var x = ran * canvas.width;
# 这里有个技巧
# 想要得到画布的65%,则添加一个系数0.65
var y = ran * canvas.height * 0.65;
var R = ran * 2 * 10;
var rotation = ran * 360;
drawStar(ctx, x, y, R, rotation);
}
tips: 想要得到星星分布在画布高度的65%,则添加一个系数0.65
具体效果:图形变换绘制星空
5.transform 变换矩阵
图形学变换矩阵是都顶点坐标进行重计算的一种方式,对于二维系统,则是 3x3 的矩阵,对于三维系统则是 4x4 的矩阵。
对于3x3 的矩阵:
其中 a, c, b, d 负责对图形进行scale, rotate, 对称,skew 等变换, a, d负责图形的缩放scale, e, f 负责对图形进行平移(translate)变换。第3行也可以进行其他操作,二维矩阵 详情。
对上图中的默认值情况,即不进行变换时,该矩阵为单位矩阵。
对于transform函数:
transform(a, b, c, d, e, f)
所有:
translate(100, 100) =>
transform(1, 0, 0, 1, 100, 100)
# a为-1, 则产生对Y轴的镜像效果
transform(-1, 0, 0, 1, 0, 0)
# 当然d的值为负数,则可以产生对x轴的镜像翻转效果
scale(1.5, 1.5) =>
transform(1.5, 0, 0, 1.5, 0, 0)
skew则改变b, c的值 =>
transform(1, 0.2, 0, 1, 0, 0)
setTransform()
同样,transform操作是叠加的,我们可以使用setTransform来设置变换效果,使之前的transform失效:
ctx.save()
ctx.transform(1, 0, 0, 1, 50, 100)
ctx.transform(2, 0, 0, 1.5, 0, 0)
# 使上面的transform失效,得到新的transform效果
ctx.setTransform(1, 0, 0, 1, 100, 100)
总结
本章的知识和css中的基础知识类似,学起来也比较容易理解,对于二维矩阵则需要多花功夫去了解。