基本画布功能
- 基本 - 设置width和height 
- 使用getContext()方法,传入“2d”获取绘图上下文引用 
- 使用时最好先测试一下getContext是否存在 
- 示例 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- //html 
 <canvas id="drawing" width="200" height="200">A drawing of something.</canvas>
 //js
 let drawing = document.getElementById("drawing");
 // 确保浏览器支持<canvas>
 if (drawing.getContext) {
 let context = drawing.getContext("2d");
 // 其他代码
 }
 
- toDataURL()方法导出canvas元素上的图像,这个方法接收一个参数:要生成的图像的MIME类型 - 示例 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- let drawing = document.getElementById("drawing"); 
 // 确保浏览器支持<canvas>
 if (drawing.getContext) {
 // 取得图像的数据 URI
 let imgURI = drawing.toDataURL("image/png");
 // 显示图片
 let image = document.createElement("img");
 image.src = imgURI;
 document.body.appendChild(image);
 }
 浏览
 
绘图上下文
填充和描边
- 2d上下文有两个基本绘制操作:填充fillStyle和描边strokeStyle 
- 示例 - 1 
 2
 3
 4
 5
 6
 7- let drawing = document.getElementById("drawing"); 
 // 确保浏览器支持<canvas>
 if (drawing.getContext) {
 let context = drawing.getContext("2d");
 context.strokeStyle = "red";
 context.fillStyle = "#0000ff";
 }- 所有与描边和填充相关的操作都会使用这两种样式,除非再次修改。 
绘制矩形
- 矩形是一个唯一可以直接在2d绘图上下文中绘制的形状 
- 与绘制矩形相关的三个方法,这些方法接收4个参数:矩形x坐标、矩形y坐标、矩形宽度、矩形高度 - fillRect()绘制矩形内容区 - 1 
 2
 3
 4
 5
 6
 7
 8- //...同上 
 //绘制红色矩形
 context.fillStyle = "#ff0000"
 context.fillRect(10,10,50,50)
 //绘制半透明蓝色矩形
 context.fillStyle = "rgba(0,0,255,0.5)"
 context.fillRect(30, 30, 50, 50);
- strokeRect() 绘制矩形轮廓 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15- // 绘制红色轮廓的矩形 
 context.strokeStyle = "#ff0000";
 //绘制
 context.strokeRect(10, 10, 50, 50);
 
 // 绘制半透明蓝色轮廓的矩形
 context.strokeStyle = "rgba(0,0,255,0.5)";
 //描边宽度 值为任意整数值
 context.lineWidth = 10
 //控制线条端点的形状
 context.lineCap = "butt" //"butt"平头 "round"出圆头 "square"出方头
 //控制线条交点形状
 context.lineJoin = "round" //"round"圆转 "bevel"取平 "miter" 出尖
 //绘制
 context.strokeRect(30, 30, 50, 50);
 
绘制路径
- 要绘制路径,必须首先调用 beginPath()方法以表示要开始绘制新路径。然后,再调用下列方法来绘制路径。 
- 方法 - arc(x, y, radius, startAngle, endAngle, counterclockwise):以坐标(x, y)为圆心,以 radius 为半径绘制一条弧线,起始角度为 startAngle,结束角度为 endAngle(都是弧度)。最后一个参数 counterclockwise 表示是否逆时针计算起始角度和结束角度(默认为顺时针)。 
- arcTo(x1, y1, x2, y2, radius):以给定半径 radius,经由(x1, y1)绘制一条从上一点到(x2, y2)的弧线。 
- bezierCurveTo(c1x, c1y, c2x, c2y, x, y):以(c1x, c1y)和(c2x, c2y)为控制点,绘制一条从上一点到(x, y)的弧线(三次贝塞尔曲线)。 
- lineTo(x, y):绘制一条从上一点到(x, y)的直线。 
- moveTo(x, y):不绘制线条,只把绘制光标移动到(x, y)。 
- quadraticCurveTo(cx, cy, x, y):以(cx, cy)为控制点,绘制一条从上一点到(x, y) 
 - 的弧线(二次贝塞尔曲线)。 - rect(x, y, width, height):以给定宽度和高度在坐标点(x, y)绘制一个矩形。这个方法与 strokeRect()和 fillRect()的区别在于,它创建的是一条路径,而不是独立的图形。创建路径之后,可以使用 closePath()方法绘制一条返回起点的线。如果路径已经完成,则既可以指定 fillStyle 属性并调用 fill()方法来填充路径,也可以指定 strokeStyle 属性并调用stroke()方法来描画路径,还可以调用 clip()方法基于已有路径创建一个新剪切区域。
 
- 示例,绘制一个不带数字的表盘 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20- 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.moveTo(100, 100);
 context.lineTo(100, 15);
 // 绘制时针
 context.moveTo(100, 100);
 context.lineTo(35, 100);
 // 描画路径
 context.stroke();
 } 
- 一个 isPointInPath()方法,接收 x 轴和 y 轴坐标作为参数。这个方法用于确定指定的点是否在路径 - 上,可以在关闭路径前随时调用 - 1 
 2
 3- if (context.isPointInPath(100, 100)) { 
 alert("Point (100, 100) is in the path.");
 }
绘制文本
- 绘制文本有fillText()和strokeText()两个方法,接收4个参数:绘制的字符串、x坐标轴、y坐标轴和最大可选像素宽度,而且这两个方法最终绘制的结果都取决于以下3个属性,这些值都有默认值,不需要每次都指定 - font 以 CSS 语法指定的字体样式、大小、字体族等,比如”10px Arial”。
- textAlign 可能的值包括”start”、”end”、”left”、”right”和 “center”。
- textBaseLine 可能的值包括 “top” 、 “hanging” 、 “middle” 、”alphabetic”、”ideographic”和”bottom”
 
- 示例 - 1 
 2
 3
 4- context.font = "bold 14px Arial"; 
 context.textAlign = "center";
 context.textBaseline = "middle";
 context.fillText("12", 100, 20); 
- 辅助确定文本大小的measureText()方法,该方法接收一个参数,即要绘制的文本,然后返回一个TextMetrics对象,这个返回对象只有一个width属性,measureText()方法使用font、textAlign和textBaseline属性当前的值计算绘制指定文本大小 - 假设要把文本”Hello world!”放到一个 140 像素宽的矩形中,可以使用以下代码, - 从 100 像素的字体大小开始计算,不断递减,直到文本大小合适: - 1 
 2
 3
 4
 5
 6
 7- let fontSize = 100 
 context.font = fontSize + "px Arial"
 while(context.measureText('Hello world!').width > 140){
 fontSize--
 context.font = fontSize + "px Arial"
 }
 context.fillText("Hello world!",10,10)- fillText()和 strokeText()方法还有第四个参数,即文本的最大宽度。这个参数是可选的(Firefox 4 是第一个实现它的浏览器),如果调用 fillText()和 strokeText()时提供了此参数,但要绘制的字符串超出了最大宽度限制,则文本会以正确的字符高度绘制,这时字符会被水平压缩,以达到限定宽度 
 
变换
- 以下方法可用于改变绘制上下文的变换矩阵。 - 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()。 
 
- 示例,在前面绘制表盘的例子中,如果把坐标原点移动到表盘中心,那再绘制表针就非常简单了: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22- 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();
 }- 把原点移动到(100, 100),也就是表盘的中心后,要绘制表针只需简单的数学计算即可。这是因为所有计算都是基于(0, 0),而不是(100, 100)了。当然,也可以使用 rotate()方法来转动表针: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24- 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.rotate(1);
 // 绘制分针
 context.moveTo(0, 0);
 context.lineTo(0, -85);
 // 绘制时针
 context.moveTo(0, 0);
 context.lineTo(-65, 0);
 // 描画路径
 context.stroke();
 } 
- save() 可以保留变换,调用这个方法后,所有的这一时刻的设置会被放到一个暂存栈中,保存之后,可以继续修改上下文,而需要恢复之前的上下文时,可以调用restore()方法,从暂存栈中取出并恢复之前保存的设置,多次调用 save()方法可以在暂存栈中存储多套设置,然后通过 restore()可以系统地恢复。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- context.fillStyle = "#ff0000"; 
 context.save();
 context.fillStyle = "#00ff00";
 context.translate(100, 100);
 context.save();
 context.fillStyle = "#0000ff";
 context.fillRect(0, 0, 100, 200); // 在(100, 100)绘制蓝色矩形
 context.restore();
 context.fillRect(10, 10, 100, 200); // 在(100, 100)绘制绿色矩形
 context.restore();
 context.fillRect(0, 0, 100, 200); // 在(0, 0)绘制红色矩形- 注意,save()方法只保存应用到绘图上下文的设置和变换,不保存绘图上下文的内容。 
绘制图像
- 使用drawImage()方法可以把现有的图像绘制到画布上,传入3个参数:要绘制的图像、绘制目标的x坐标,y坐标 
- //最简单的调用是传入一个HTML的img元素 let image = document.images[0]; context.drawImage(image, 10, 10); <!--12-->
- 还可以只把图像绘制到上下文中的一个区域。此时,需要给 drawImage()提供 9 个参数:要绘制的图像、源图像 x 坐标、源图像 y 坐标、源图像宽度、源图像高度、目标区域 x 坐标、目标区域 y 坐标、 - 目标区域宽度和目标区域高度。 - 1 - context.drawImage(image, 0, 10, 50, 50, 0, 100, 40, 60); 
- 第一个参数除了可以是 HTML 的 - img元素,还可以是另一个- canvas元素,这样就会把另一个画布的内容绘制到当前画布上
- 操作的结果可以使用toDataURL()方法获取。不过有一种情况例外:如果绘制的图像来自其他域而非当前页面,则不能获取其数据。此时,调用 toDataURL()将抛出错误。比如,如果来自 www.example.com 的页面上绘制的是来自 www.wrox.com 的图像,则上下文就是“脏的”,获取数据时会抛出错误。 
阴影
- 2D 上下文可以根据以下属性的值自动为已有形状或路径生成阴影。 - shadowColor:CSS 颜色值,表示要绘制的阴影颜色,默认为黑色。 
- shadowOffsetX:阴影相对于形状或路径的 x 坐标的偏移量,默认为 0。 
- shadowOffsetY:阴影相对于形状或路径的 y 坐标的偏移量,默认为 0。 
- shadowBlur:像素,表示阴影的模糊量。默认值为 0,表示不模糊。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- let context = drawing.getContext("2d"); 
 // 设置阴影
 context.shadowOffsetX = 5;
 context.shadowOffsetY = 5;
 context.shadowBlur = 4;
 context.shadowColor = "rgba(0, 0, 0, 0.5)";
 // 绘制红色矩形
 context.fillStyle = "#ff0000";
 context.fillRect(10, 10, 50, 50);
 // 绘制蓝色矩形
 context.fillStyle = "rgba(0,0,255,1)";
 context.fillRect(30, 30, 50, 50);
 
渐变
基础渐变
- 渐变通过 CanvasGradient 的实例表示,在 2D 上下文中创建和修改都非常简单。要创建一个新的线性渐变,可以调用上下文的 createLinearGradient()方法。这个方法接收 4 个参数:起点 x 坐标、起点 y 坐标、终点 x 坐标和终点 y 坐标。调用之后,该方法会以指定大小创建一个新的 CanvasGradient 
- 有了 gradient 对象后,接下来要使用 addColorStop()方法为渐变指定色标。这个方法接收两 - 个参数:色标位置和 CSS 颜色字符串。色标位置通过 0~1 范围内的值表示,0 是第一种颜色,1 是最后 - 一种颜色 
- 示例 - 1 
 2
 3
 4- 一种颜色。比如: 
 let gradient = context.createLinearGradient(30, 30, 70, 70);
 gradient.addColorStop(0, "white");
 gradient.addColorStop(1, "black");- 这个 gradient 对象现在表示的就是在画布上从(30, 30)到(70, 70)绘制一个渐变。渐变的起点颜色为白色,终点颜色为黑色。可以把这个对象赋给 fillStyle 或 strokeStyle 属性,从而以渐变填充或描画绘制的图形:对象并返回实例。 - 1 
 2
 3
 4
 5
 6- // 绘制红色矩形 
 context.fillStyle = "#ff0000";
 context.fillRect(10, 10, 50, 50);
 // 绘制渐变矩形
 context.fillStyle = gradient;
 context.fillRect(30, 30, 50, 50);- 保持渐变与形状的一致非常重要,有时候可能需要写个函数计算相应的坐标 - 1 
 2
 3- function createRectLinearGradient(context, x, y, width, height) { 
 return context.createLinearGradient(x, y, x+width, y+height);
 }- 这个函数会基于起点的 x、y 坐标和传入的宽度、高度创建渐变对象,之后调用 fillRect()方法时可以使用相同的值: - 1 
 2
 3
 4
 5
 6- let gradient = createRectLinearGradient(context, 30, 30, 50, 50); 
 gradient.addColorStop(0, "white");
 gradient.addColorStop(1, "black");
 // 绘制渐变矩形
 context.fillStyle = gradient;
 context.fillRect(30, 30, 50, 50);
径向渐变
- 径向渐变(或放射性渐变)要使用 createRadialGradient()方法来创建。这个方法接收 6 个参数,分别对应两个圆形圆心的坐标和半径。前 3 个参数指定起点圆形中心的 x、y 坐标和半径,后 3 个参数指定终点圆形中心的 x、y 坐标和半径。 - 1 
 2
 3
 4
 5
 6
 7
 8- let gradient = context.createRadialgradient.addColorStop(0, "white"); 
 gradient.addColorStop(1, "black");
 // 绘制红色矩形
 context.fillStyle = "#ff0000";
 context.fillRect(10, 10, 50, 50);
 // 绘制渐变矩形
 context.fillStyle = gradient;
 context.fillRect(30, 30, 50, 50);
图案
- 图案是用于填充和描画图形的重复图像。要创建新图案。可以调用 createPattern()方法并传入两个参数:一个 HTML - 元素和一个表示该如何重复图像的字符串。第二个参数的值与 CSS 的background-repeat 属性是一样的,包括”repeat”、”repeat-x”、”repeat-y”和”no-repeat”。 - 1 
 2
 3
 4
 5
 6- let image = document.images[0], 
 //传给 createPattern()方法的第一个参数也可以是<video>元素或者另一个<canvas>元素。
 pattern = context.createPattern(image, "repeat");
 // 绘制矩形
 context.fillStyle = pattern;
 context.fillRect(10, 10, 150, 150);
图像数据
- 2D 上下文中比较强大的一种能力是可以使用 getImageData()方法获取原始图像数据。 - 1 
 2
 3
 4
 5
 6
 7- let imageData = context.getImageData(10, 5, 50, 50); 
 //其中,data 属性是包含图像的原始像素信息的数组。每个像素在 data 数组中都由 4 个值表示,分别代表红、绿、蓝和透明度值。换句话说,第一个像素的信息包含在第 0 到第 3 个值中,比如:
 let data = imageData.data,
 red = data[0],
 green = data[1],
 blue = data[2],
 alpha = data[3];- 这个数组中的每个值都在 0~255 范围内(包括 0 和 255)。对原始图像数据进行访问可以更灵活地操作图像。例如,通过更改图像数据可以创建一个简单的灰阶过滤器: - 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- let drawing = document.getElementById("drawing"); 
 // 确保浏览器支持<canvas>
 if (drawing.getContext) {
 let context = drawing.getContext("2d"),
 image = document.images[0],
 imageData, data,
 i, len, average,
 red, green, blue, alpha;
 // 绘制图像
 context.drawImage(image, 0, 0);
 // 取得图像数据
 imageData = context.getImageData(0, 0, image.width, image.height);
 data = imageData.data;
 for (i=0, len=data.length; i < len; i+=4) {
 red = data[i];
 green = data[i+1];
 blue = data[i+2];
 alpha = data[i+3];
 // 取得 RGB 平均值
 average = Math.floor((red + green + blue) / 3);
 // 设置颜色,不管透明度
 data[i] = average;
 data[i+1] = average;
 data[i+2] = average;
 }
 // 将修改后的数据写回 ImageData 并应用到画布上显示出来
 imageData.data = data;
 context.putImageData(imageData, 0, 0);
 }- 这个例子首先在画布上绘制了一个图像,然后又取得了其图像数据。for 循环遍历了图像数据中的每个像素,注意每次循环都要给 i 加上 4。每次循环中取得红、绿、蓝的颜色值,计算出它们的平均值。然后再把原来的值修改为这个平均值,实际上相当于过滤掉了颜色信息,只留下类似亮度的灰度信息。之后将 data 数组重写回 imageData 对象。最后调用 putImageData()方法,把图像数据再绘制到画布上。结果就得到了原始图像的黑白版。 - 注意:只有在画布没有加载跨域内容时才可以获取图像数据。如果画布上绘制的是跨域内 
 容,则尝试获取图像数据会导致 JavaScript 报错。
合成
- globalAlpha 属性是一个范围在 0~1 的值(包括 0 和 1),用于指定所有绘制内容的透明度,默认值为 0。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- // 绘制红色矩形 
 context.fillStyle = "#ff0000";
 context.fillRect(10, 10, 50, 50);
 // 修改全局透明度
 context.globalAlpha = 0.5;
 // 绘制蓝色矩形
 context.fillStyle = "rgba(0,0,255,1)";
 context.fillRect(30, 30, 50, 50);
 // 重置
 context.globalAlpha = 0;
- globalCompositionOperation 属性表示新绘制的形状如何与上下文中已有的形状融合。 - source-over:默认值,新图形绘制在原有图形上面。 
- source-in:新图形只绘制出与原有图形重叠的部分,画布上其余部分全部透明。 
- source-out:新图形只绘制出不与原有图形重叠的部分,画布上其余部分全部透明。 
- source-atop:新图形只绘制出与原有图形重叠的部分,原有图形不受影响。 
- destination-over:新图形绘制在原有图形下面,重叠部分只有原图形透明像素下的部分可见。 
- destination-in:新图形绘制在原有图形下面,画布上只剩下二者重叠的部分,其余部分完全透明。 
- destination-out:新图形与原有图形重叠的部分完全透明,原图形其余部分不受影响。 
- destination-atop:新图形绘制在原有图形下面,原有图形与新图形不重叠的部分完全透明。 
- lighter:新图形与原有图形重叠部分的像素值相加,使该部分变亮。 
- copy:新图形将擦除并完全取代原有图形。 
- xor:新图形与原有图形重叠部分的像素执行“异或”计算。 
 



