Canvas 是什么?
- Canvas 中文名叫 “画布”,是 HTML5 新增的一个标签。
- Canvas 允许开发者通过 JS在这个标签上绘制各种图案。
- Canvas 拥有多种绘制路径、矩形、圆形、字符以及图片的方法。
- Canvas 在某些情况下可以 “代替” 图片。
- Canvas 可用于动画、游戏、数据可视化、图片编辑器、实时视频处理等领域。
Canvas 和 SVG 的区别
| Canvas | SVG | | —- | —- | | 用JS动态生成元素(一个HTML元素) | 用XML描述元素(类似HTML元素那样,可用多个元素来描述一个图形) | | 位图(受屏幕分辨率影响) | 矢量图(不受屏幕分辨率影响) | | 不支持事件 | 支持事件 | | 数据发生变化需要重绘 | 不需要重绘 |
就上面的描述而言可能有点难懂,你可以打开 AntV 旗下的图形编辑引擎做对比。G6[1] 是使用 canvas 开发的,X6[2] 是使用 svg 开发的。
我的建议是:如果要展示的数据量比较大,比如一条数据就是一个元素节点,那使用 canvas 会比较合适;如果用户操作的交互比较多,而且对清晰度有要求(矢量图),那么使用 svg 会比较合适。
canvas基本的画布功能
创建 元素时至少要设置其 width 和 height 属 性,这样才能告诉浏览器在多大面积上绘图。
出现在开始和结束标签 之间的内容是后备数据,会在浏览器不支持 元素时显 示。
<canvas id="drawing" width="200" height="200">Adrawing of something.</canvas>
getContext() 获取绘图上下文
要在画布上绘制图形,首先要取得绘图上下文。使用 getContext() 方法可以获取对绘图上下文的引用。对于平面图 形,需要给这个方法传入参数 “2d” ,表示要获取2D上下文对象。
let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
let context = drawing.getContext("2d");
// 其他绘制代码
}
使用 元素时,最好先测试一下 getContext() 方 法是否存在。有些浏览器对HTML规范中没有的元素会创建默认 HTML元素对象。这就意味着即使 drawing 包含一个有效的元素引 用, getContext() 方法也未必存在。
2D绘图上下文
2D绘图上下文提供了绘制2D图形的方法,包括矩形、弧形和路 径。2D上下文的坐标原点(0, 0)在 元素的左上角。所有坐 标值都相对于该点计算。默认情况下, width 和 height 表示两个方向上像 素的最大值。
填充fillStyle和描边strokeStyle
2D上下文有两个基本绘制操作:填充和描边。填充以指定样式 (颜色、渐变或图像)自动填充形状,而描边只为图形边界着色。大 多数2D上下文操作有填充和描边的变体,显示效果取决于两个属 性: fillStyle 和 strokeStyle 。
这两个属性可以是字符串、渐变对象或图案对象,默认值都 为 “#000000” 。字符串表示颜色值,可以是CSS支持的任意格式: 名称、十六进制代码、 rgb 、 rgba 、 hsl 或 hsla 。
context.strokeStyle = "red";
context.fillStyle = "#0000ff";
绘制矩形
矩形是唯一一个可以直接在2D绘图上下文中绘制的形状。与绘制 矩形相关的方法有3个: fillRect() 、 strokeRect() 和 clearRect() 。这些方法都接收4个参数:矩形 x 坐标、矩形 y 坐标、矩形宽度和矩形高度。这几个参数的单位都是像素。
fillRect() 填充矩形
fillRect() 方法用于以指定颜色在画布上绘制并填充矩形。 填充的颜色使用 fillStyle 属性指定。
// 绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
// 绘制半透明蓝色矩形
context.fillStyle = "rgba(0,0,255,0.5)";
context.fillRect(30, 30, 50, 50);
strokeRect() 描边(轮廓)矩形
strokeRect() 方法使用通过 strokeStyle 属性指定的颜色 绘制矩形轮廓。
// 绘制红色轮廓的矩形
context.strokeStyle = "#ff0000";
context.strokeRect(10, 10, 50, 50);
// 绘制半透明蓝色轮廓的矩形
context.strokeStyle = "rgba(0,0,255,0.5)";
context.strokeRect(30, 30, 50, 50);
clearRect() 方法可以擦除画布中某个区域
使用 clearRect() 方法可以擦除画布中某个区域。该方法用于 把绘图上下文中的某个区域变透明。通过先绘制形状再擦除指定区 域,可以创建出有趣的效果,比如从已有矩形中开个孔。
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);
// 在前两个矩形重叠的区域擦除一个矩形区域
context.clearRect(40, 40, 10, 10);
}
绘制路径
2D绘图上下文支持很多在画布上绘制路径的方法。通过路径可以 创建复杂的形状和线条。
beginPath()开始绘制
要绘制路径,必须首先调用 beginPath()
方法以表示要开始绘制新路径。然后,再调用下列方法来绘制路径。
closePath()返回起点的线
下面这个例子使用前面提到的方法绘制了一个不带数字的表盘:
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();
}
这个例子使用 arc() 绘制了两个圆形,一个外圆和一个内圆, 以构成表盘的边框。外圆半径99像素,原点为(100,100),也就是画布 的中心。要绘制完整的圆形,必须从0弧度绘制到2π弧度(使用数学常 量 Math.PI )。而在绘制内圆之前,必须先把路径移动到内圆上的 一点,以避免绘制出多余的线条。第二次调用 arc() 时使用了稍小 一些的半径,以呈现边框效果。然后,再组合运用 moveTo() 和 lineTo() 分别绘制分针和时针。最后一步是调用 stroke()
绘制文本
文本和图像混合也是常见的绘制需求,因此2D绘图上下文还提供 了绘制文本的方法,即 fillText() 和 strokeText() 。这两个 方法都接收4个参数:要绘制的字符串、 x 坐标、 y 坐标和可选的最大像素宽度。
这些属性都有相应的默认值,因此没必要每次绘制文本时都设置 它们。 fillText() 方法使用 fillStyle 属性绘制文本,而 strokeText() 方法使用 strokeStyle 属性。
fillText填充文本
strokeText描边文本
measureText()
由于绘制文本很复杂,特别是想把文本绘制到特定区域的时候, 因此2D上下文提供了用于辅助确定文本大小的 measureText() 方 法。这个方法接收一个参数,即要绘制的文本,然后返回一个 TextMetrics 对象。这个返回的对象目前只有一个属性 width , 不过将来应该会增加更多度量指标。 measureText() 方法使用 font 、 textAlign 和 textBaseline 属性当前的值计算绘制指定文本后的大小。
变换
上下文变换可以操作绘制在画布上的图像。2D绘图上下文支持所 有常见的绘制变换。在创建绘制上下文时,会以默认值初始化变换矩 阵,从而让绘制操作如实应用到绘制结果上。对绘制上下文应用变 换,可以导致以不同的变换矩阵应用绘制操作,从而产生不同的结 果。 以下方法可用于改变绘制上下文的变换矩阵。
绘制图像
2D绘图上下文内置支持操作图像。如果想把现有图像绘制到画布 上,可以使用 drawImage() 方法。这个方法可以接收3组不同的参 数,并产生不同的结果。最简单的调用是传入一个HTML的 元素,以及表示绘制目标的 x 和 y 坐标,结果是把图像绘制到指定位置。
let image = document.images[0];
context.drawImage(image, 10, 10);
以上代码获取了文本中的第一个图像,然后在画布上的坐标(10, 10)处将它绘制了出来。绘制出来的图像与原来的图像一样大。如果想 改变所绘制图像的大小,可以再传入另外两个参数:目标宽度和目标 高度。这里的缩放只影响绘制的图像,不影响上下文的变换矩阵。
context.drawImage(image, 50, 10, 20, 30);
还可以只把图像绘制到上下文中的一个区域。此时,需要给 drawImage() 提供9个参数:要绘制的图像、源图像 x 坐标、源图像 y 坐标、源图像宽度、源图像高度、目标区域 x 坐标、目标区域 y 坐标、目标区域宽度和目标区域高度。这个重载后的 drawImage() 方法可以实现最大限度的控制
context.drawImage(image, 0, 10, 50, 50, 0, 100,40, 60);
最终,原始图像中只有一部分会绘制到画布上。这一部分从(0, 10)开始,50像素宽、50像素高。而绘制到画布上时,会从(0, 100)开 始,变成40像素宽、60像素高。
阴影
2D上下文可以根据以下属性的值自动为已有形状或路径生成阴 影。
这些属性都可以通过 context 对象读写。只要在绘制图形或路 径前给这些属性设置好适当的值,阴影就会自动生成。
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
渐变通过 CanvasGradient 的实例表示,在2D上下文中创建和 修改都非常简单。要创建一个新的线性渐变,可以调用上下文的 createLinearGradient() 方法。这个方法接收4个参数:起点 x 坐标、起点 y 坐标、终点 x 坐标和终点 y 坐标。调用之后,该方法会以指定大小创建一个新的 CanvasGradient 对象并返回实例。
有了 gradient 对象后,接下来要使用 addColorStop() 方 法为渐变指定色标。这个方法接收两个参数:色标位置和CSS颜色字 符串。色标位置通过0~1范围内的值表示,0是第一种颜色,1是最后 一种颜色。
let gradient = context.createLinearGradient(30,30, 70, 70);
gradient.addColorStop(0,"white");
gradient.addColorStop(1,"black");
这个 gradient 对象现在表示的就是在画布上从(30, 30)到(70, 70)绘制一个渐变。渐变的起点颜色为白色,终点颜色为黑色。可以把 这个对象赋给 fillStyle 或 strokeStyle 属性,从而以渐变填 充或描画绘制的图形
// 绘制红色矩形
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” 。
let image = document.images[0],
pattern = context.createPattern(image,
"repeat");
// 绘制矩形
context.fillStyle = pattern;
context.fillRect(10, 10, 150, 150);
跟渐变一样,图案的起点实际上是画布的原点(0, 0)。将填 充样式设置为图案,表示在指定位置而不是开始绘制的位置显示图 案。
图像数据
getImageData()
返回 ImageData 对象,该对象为画布上指定矩形区域的复制像素数据。
2D上下文中比较强大的一种能力是可以使用 getImageData() 方法获取原始图像数据。这个方法接收4个参数:要取得数据中第一个 像素的左上角坐标和要取得的像素宽度及高度。例如,要从(10, 5)开 始取得50像素宽、50像素高的区域对应的数据
let imageData = context.getImageData(10, 5, 50,50);
返回的对象是一个 ImageData 的实例。每个 ImageData 对象 都包含3个属性: width 、 height 和 data ,其中, data 属性 是包含图像的原始像素信息的数组。每个像素在 data 数组中都由4 个值表示,分别代表红、绿、蓝和透明度值。换句话说,第一个像素 的信息包含在第0到第3个值中
- R - 红色(0-255)
- G - 绿色(0-255)
- B - 蓝色(0-255)
- A - alpha 通道(0-255; 0 是透明的,255 是完全可见的)
这个数组中的每个值都在0~255范围内(包括0和255)。对原始 图像数据进行访问可以更灵活地操作图像。例如,通过更改图像数据 可以创建一个简单的灰阶过滤器 ```javascript let drawing = document.getElementById(“drawing”); // 确保浏览器支持let data = imageData.data,
red = data[0],
green = data[1],
blue = data[2],
alpha = data[3];
这个例子首先在画布上绘制了一个图像,然后又取得了其图像数 据。 for 循环遍历了图像数据中的每个像素,注意每次循环都要给 i 加上4。每次循环中取得红、绿、蓝的颜色值,计算出它们的平均 值。然后再把原来的值修改为这个平均值,实际上相当于过滤掉了颜 色信息,只留下类似亮度的灰度信息。之后将 data 数组重写回 imageData 对象。最后调用 putImageData() 方法,把图像数据 再绘制到画布上。结果就得到了原始图像的黑白版。 当然,灰阶过滤只是基于原始像素值可以实现的其中一种操作。
<a name="L5M1g"></a>
#### createImageData()
创建新的、空白的 ImageData 对象
<a name="ufI9s"></a>
#### putImageData()
把图像数据(从指定的 ImageData 对象)放回画布上
<a name="swwk2"></a>
## toDataURL() 导出图像
可以使用 toDataURL() 方法导出 元素上的图像。 这个方法接收一个参数:要生成图像的MIME类型(与用来创建图形 的上下文无关)。例如,要从画布上导出一张PNG格式的图片,可以 这样做:
```javascript
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);
}
浏览器默认将图像编码为PNG格式,除非另行指定。Firefox和 Opera还支持传入 “image/jpeg” 进行JPEG编码。因为这个方法是 后来才增加到规范中的,所以支持的浏览器也是在后面的版本实现 的,包括IE9、Firefox 3.5和Opera 10。