本文继续对JavaScript高级程序设计第四版 第十八章第3节 进行学习
1.填充和描边
2D 上下文有两个基本绘制操作:填充和描边。填充以指定样式(颜色、渐变或图像)自动填充形状,而描边只为图形边界着色。大多数 2D 上下文操作有填充和描边的变体,显示效果取决于两个属性:fillStyle 和 strokeStyle。
这两个属性可以是字符串、渐变对象或图案对象,默认值都为"#000000"。字符串表示颜色值,可以是 CSS 支持的任意格式:名称、十六进制代码、rgb、rgba、hsl 或 hsla。比如:
let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
let context = drawing.getContext("2d");
context.strokeStyle = "red";
context.fillStyle = "#0000ff";
}
这里把 strokeStyle 设置为"red"(CSS 颜色名称),把 fillStyle 设置为"#0000ff"(蓝色)。
所有与描边和填充相关的操作都会使用这两种样式,除非再次修改。这两个属性也可以是渐变或图案,本章后面会讨论。
2.绘制矩形
矩形是唯一一个可以直接在 2D 绘图上下文中绘制的形状。与绘制矩形相关的方法有 3 个:fillRect()、strokeRect()和 clearRect()。这些方法都接收 4 个参数:矩形 x 坐标、矩形 y 坐标、矩形宽度和矩形高度。这几个参数的单位都是像素。
fillRect()方法用于以指定颜色在画布上绘制并填充矩形。填充的颜色使用 fillStyle 属性指定。来看下面的例子:
let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
let context = drawing.getContext("2d");
/*
* 引自 MDN 文档
*/
// 绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
// 绘制半透明蓝色矩形
context.fillStyle = "rgba(0,0,255,0.5)";
context.fillRect(30, 30, 50, 50);
}
以上代码先将 fillStyle 设置为红色并在坐标点(10, 10)绘制了一个宽高均为 50 像素的矩形。接着,使用 rgba()格式将 fillStyle 设置为半透明蓝色,并绘制了另一个与第一个部分重叠的矩形。结果就是可以透过蓝色矩形看到红色矩形。
strokeRect()方法使用通过 strokeStyle 属性指定的颜色绘制矩形轮廓。下面是一个例子:
let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
let context = drawing.getContext("2d");
/*
* 引自 MDN 文档
*/
// 绘制红色轮廓的矩形
context.strokeStyle = "#ff0000";
context.strokeRect(10, 10, 50, 50);
// 绘制半透明蓝色轮廓的矩形
context.strokeStyle = "rgba(0,0,255,0.5)";
context.strokeRect(30, 30, 50, 50);
}
以上代码同样绘制了两个重叠的矩形,不过只有轮廓,而不是实心的
使用 clearRect()方法可以擦除画布中某个区域。该方法用于把绘图上下文中的某个区域变透明。
通过先绘制形状再擦除指定区域,可以创建出有趣的效果,比如从已有矩形中开个孔。
3.绘制路径
2D 绘图上下文支持很多在画布上绘制路径的方法。通过路径可以创建复杂的形状和线条。要绘制路径,必须首先调用 beginPath()方法以表示要开始绘制新路径。然后,再调用下列方法来绘制路径。
- arc(x, y, radius, startAngle, endAngle, counterclockwise):
- arcTo(x1, y1, x2, y2, radius):
- bezierCurveTo(c1x, c1y, c2x, c2y, x, y)
- lineTo(x, y):
- moveTo(x, y):
- quadraticCurveTo(cx, cy, x, y):
- rect(x, y, width, height):以给定宽度和高度在坐标点(x, y)绘制一个矩形。这个方法与 strokeRect()和 fillRect()的区别在于,它创建的是一条路径,而不是独立的图形。
创建路径之后,可以使用 closePath()方法绘制一条返回起点的线。
var ctx = document. getElementById ( 'cvs' ). getContext ( '2d' ) ;
ctx. beginPath ( ) ;
ctx. moveTo ( 100.5 , 20.5 ) ;
ctx. lineTo ( 200.5 , 20.5 ) ;
//stroke() 方法会实际地绘制出通过 moveTo() 和 lineTo() 方法定义的路径。默认颜色是黑色。
ctx. stroke ( ) ;
ctx. moveTo ( 100.5 , 40.5 ) ;
ctx. lineTo ( 200.5 , 40.5 )
ctx. strokeStyle = '#f00' ;
ctx. stroke ( ) ;
上面的代码会得到什么样的图形呢?是不是一条黑线一条红线呢?从代码上看,我们的逻辑毫无问题,但结果是我们得到的是两条红线,并不是一黑一红。如果你明白这是为什么,那后面的你就不用看了。这就是beginPath的重要性。
canvas中的绘制方法(如stroke,fill),都会以“上一次beginPath”之后的所有路径为基础进行绘制。比如上面的代码里面我stroke了两次,其实这两次都是以第一次beginPath后的所有路径为基础画的。也就是说第一条路径我们stroke了两下,第一下是黑的,第二下是红的,所以最终也是红的。
- 不管你用moveTo把画笔移动到哪里,只要不beginPath,那你一直都是在画一条路径。
- fillRect与strokeRect这种直接画出独立区域的函数,也不会打断当前的path.
说到beginPath,就不得不提到closePath,两者是不是有很“紧”的联系呢?答案是几乎没有关系。closePath的意思不是结束路径,而是关闭路径,它会试图从当前路径的终点连一条路径到起点,让整个路径闭合起来。但是,这并不意味着它之后的路径就是新路径了!
我们在上面的代码的第一个lineTo后面加上closePath,可以发现还是得到了两条红线。但如果我们在第一个stroke后面加上beginPath,则会如愿得到一条黑线一条红线。
ctx. stroke ( ) ;
ctx. beginPath ( ) ; //注意啦!
ctx. moveTo ( 100.5 , 40.5 ) ;
ctx. lineTo ( 200.5 , 40.5 )
ctx. strokeStyle = '#f00' ;
ctx. stroke ( ) ;
总而言之,就是不要企图通过闭合现有路径来开始一条新路径,而开始一条新路径,以前的路径也不会闭合。
4.绘制文本
fillText()方法使用fillStyle 属性绘制文本,而 strokeText()方法使用 strokeStyle 属性。通常,fillText()方法是使用最多的,因为它模拟了在网页中渲染文本。例如,下面的例子会在前一节示例的表盘顶部绘制数字“12”:
context.font = "bold 14px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("12", 100, 20);
5.变换
上下文变换可以操作绘制在画布上的图像。2D 绘图上下文支持所有常见的绘制变换。在创建绘制上下文时,会以默认值初始化变换矩阵,从而让绘制操作如实应用到绘制结果上。对绘制上下文应用变换,可以导致以不同的变换矩阵应用绘制操作,从而产生不同的结果。以下方法可用于改变绘制上下文的变换矩阵。
- rotate(angle):围绕原点把图像旋转 angle 弧度。
- scale(scaleX, scaleY):通过在 x 轴乘以 scaleX、在 y 轴乘以 scaleY 来缩放图像。scaleX和 scaleY 的默认值都是 1.0。
- translate(x, y):把原点移动到(x, y)。执行这个操作后,坐标(0, 0)就会变成(x, y)。
- transform(m1_1, m1_2, m2_1, m2_2, dx, dy):像下面这样通过矩阵乘法直接修改矩阵。
m1_1 m1_2 dx
m2_1 m2_2 dy
0 0 1
- setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy):把矩阵重置为默认值,再以传入的参数调用 transform()。
变换可以简单,也可以复杂。例如,在前面绘制表盘的例子中,如果把坐标原点移动到表盘中心,那再绘制表针就非常简单了:
let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
let context = drawing.getContext("2d");
// 创建路径
context.beginPath();
// 绘制外圆
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
// 绘制内圆
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
// 移动原点到表盘中心
context.translate(100, 100);
// 绘制分针
context.moveTo(0, 0);
context.lineTo(0, -85);
// 绘制时针
context.moveTo(0, 0);
context.lineTo(-65, 0);
// 描画路径
context.stroke();
}
6.save/restore
save方法可以理解为暂存当前画笔的状态,接下来对画笔的操作都不会被保存下来,直到restore方法被调用。讲得通俗一点,就是说,调用save方法,就是把当前的笔放笔架上,换一支笔,调用restore方法时,再把刚才放到笔架上的笔再拿下来用。
7.绘制图像
let image = document.images[0];
context.drawImage(image, 10, 10);
8.绘制曲线
- beginPath 新建一条路径
- closePath 将路径移动到起始点
- moveTo 将路径移动到指定的坐标(x, y)
- lineTo 添加直线至路径
- rect 添加矩形至路径
- arc 添加圆形至路径
- ellipse 添加椭圆至路径
- bezierCurveTo 添加三次贝塞尔曲线至路径
- quadraticCurveTo 添加二次贝塞尔曲线至路径
- stroke 通过线条来绘制图形轮廓
- fill 通过填充路径的内容区域生成实心的图形