Canvas绘图(二)
本章将学习Canvas绘图的以下技巧
- 裁剪区域
- 图象合成
- 简单的Canvas变换
一、设置裁切区域
使用Canvas裁切区域可以限制路径以及子路径的绘制区域,通过rect()
函数设置一个用来绘图的矩形区域环境属性;然后调用clip()
函数用rect()
函数定义的矩形区域设置为裁切区域。那么,无论当前环境绘制什么内容,他只显示裁切区域以内的部分,也可以理解成绘图操作的一种蒙版。例如
drawClip() {
// 绘制一个大方块
this.context.fillStyle = "#66ccff";
this.context.fillRect(10, 10, 200, 200);
this.context.save();
this.context.beginPath();
// 裁切画布从(0,0)点到50 x 50的正方形
this.context.rect(0, 0, 50, 50);
this.context.clip();
// 红色圆
this.context.beginPath();
this.context.strokeStyle = "red";
this.context.lineWidth = 5;
this.context.arc(100, 100, 100, 0, (Math.PI / 180) * 360, false);
// 整圆
this.context.stroke();
this.context.closePath();
this.context.restore();
// 再次裁切整个画布
this.context.beginPath();
this.context.rect(0, 0, 500, 500);
this.context.clip();
// 裁切一个没有 裁切的 蓝线
this.context.beginPath();
this.context.strokeStyle = "blue";
this.context.lineWidth = 5;
this.context.arc(100, 100, 50, 0, (Math.PI / 180) * 360, false);
this.context.stroke();
this.context.closePath();
}
首先,我们创建了50*50的矩形区域作为裁切区域,然后绘制一个红色圆弧形,但是所画的红色圆弧线只能看到在这个矩形以内的部分(有点类似overflow :hidden
),最后将裁切区域调整为500 * 500,画一个蓝色圆弧。
二、合成
合成是指如何精细控制画布上对象的透明层与分层效果,有两个属性可以控制。 在Canvas中,默认情况下把Canvas之中的某个物体源绘制到另一个物体(目标)上时,浏览器就会简单的把源物体叠放在目标图像上。
另外,Canvas每次绘制一个图形,就会将原有的Canvas图像作为目标图像,将将要绘制的图像作为源图像
- globalAlpha : 该属性的默认值为1.0(完全不透明),取值范围为0.,0 ~1.0
- globalCompositeOperation : 该属性在globalAlpah以及所有变换生效后,控制如何在当前Canvas位图中绘制图形,它由以下枚举值
属性 | 说明 |
---|---|
source-over(默认) | 默认。在目标图像上显示源图像。 |
source-atop | 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。 |
source-out | 在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。 |
source-in | 在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。 |
destination-over | 在源图像上方显示目标图像。 |
destination-atop | 在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。 |
destination-in | 在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的 |
destination-out | 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。 |
copy | 显示源图像。忽略目标图像。 |
xor | 使用异或操作对源图像与目标图像进行组合。 |
lighter | 显示源图像 + 目标图像。 |
A. souce-over
this.context.fillStyle = "black";
this.context.fillRect(10, 10, 200, 200);
// 红色正方形会叠加到黑色上面
this.context.globalCompositeOperation = "source-over";
this.context.fillStyle = "red";
this.context.fillRect(1, 1, 50, 50);
B. souce-atop
this.context.globalCompositeOperation = "source-atop";
this.context.fillRect(60, 1, 50, 50);
C. source-in
只有目标图像内的源图像部分会显示,目标图像是透明的
this.context.globalCompositeOperation = "source-in";
this.context.fillRect(1, 60, 50, 50);
D. destination-over
this.context.globalCompositeOperation = 'destination-over'
this.context.fillRect(1, 1, 50, 50);
E. destination-atop
this.context.globalCompositeOperation = 'destination-atop'
this.context.fillRect(1, 1, 50, 50);
F. destination-in
this.context.globalCompositeOperation = 'destination-in'
this.context.fillRect(1, 1, 50, 50);
G.destination-out
this.context.globalCompositeOperation = 'destination-out'
this.context.fillRect(1, 1, 50, 50);
三、简单画布转换
画布变换是指用数学方法调整绘制形状的物理数学,例如缩放与旋转,所有变换依赖后台的数学矩阵运算,我们不必去理解这些运算,我们将讨论如何调整Canvas属性来应用旋转和缩放变换。
这里说一下 Canvas坐标变换的方式 :
- 平移 : translate
- 缩放 : scale
- 旋转 : rotate
而上面三种方式,都可以通过transform()矩阵变换做到,transform(m11,m12,m22,dx,dy),这个之后单独开一章节讲。
A. 旋转和平移变换
例如,我们对一个正方形进行旋转 .
const x = 100,
y = 100,
width = 50,
height = 50,
angleRadians = (45 * Math.PI) / 180;
this.context.translate(x + 0.5 * width, y + 0.5 * height);
this.context.rotate(angleRadians);
this.context.fillStyle = "red";
this.context.fillRect(-0.5 *width, -0.5 * height, width, height);
值得注意以下几点 :
-
translate()
: 可以设置画布的原点,默认(0,0),这里设置为红色正方形的中点,那么就可以实现围绕中心点进行旋转了 -
fillRect(-05 * width,-0.5 * height...)
: 为什么绘制的原点是(-25,-25)呢?是因为原点改变了,现在是(125,125),所以要从(-25,-25)开始绘制
例如绘制多个正方形
drawRotateRects() {
this.drawRect(50, 100, 40, 40, 45);
this.drawRect(100, 100, 40, 40, 75);
this.drawRect(150, 100, 40, 40, 90);
this.drawRect(200, 100, 40, 40, 120);
}
drawRect(x, y, width, height, angle) {
this.context.setTransform(1, 0, 0, 1, 0, 0);
const angleInRadians = (angle * Math.PI) / 180;
this.context.translate(x + 0.5 * width, y + 0.5 * height);
this.context.rotate(angleInRadians);
this.context.fillStyle = "red";
this.context.fillRect(-0.5 * width, -0.5 * height, width, height);
}
this.context.setTransform(1, 0, 0, 1, 0, 0);
这段代码你可以理解为重置Canvas变换设置
B. 缩放变换
接口 | 说明 |
---|---|
context.scale(x,y) | x为x轴的缩放属性,y为y轴缩放属性,默认为1 如果要把一个对象放大两倍,则可以将两个参数都设为2 |
drawScaleRect() {
this.context.fillRect(100, 100, 50, 50);
this.context.setTransform(1, 0, 0, 1, 0, 0);
this.context.scale(2, 2);
this.context.fillStyle = "red";
this.context.fillRect(100, 100, 50, 50);
}
这段代码工作方式与旋转差不多,由于没有平移原点对正方形进行缩放,而仍然用画布左上角作为原点,因此红色的正方形向右下方移动了。可以从中心点进行缩放,代码如下所示
drawScaleRectByCenter() {
this.context.fillRect(100, 100, 50, 50);
this.context.globalCompositeOperation = 'destination-over'
this.context.setTransform(1, 0, 0, 1, 0, 0);
const x = 100,
y = 100,
width = 50,
height = 50;
this.context.translate(x + 0.5 * width, y + 0.5 * height);
this.context.scale(2, 2);
this.context.fillStyle = "red";
this.context.fillRect(-0.5 * width, -0.5 * height, width, height);
}
最佳实践 : 任何形状的中心点都是
(x + 0.5 * width, y + 0.5 * height)