一、<canvas> 元素
在html中添加canvas元素
<canvas id="canvas" width="150" height="150">
您的浏览器不支持 Canvas!
</canvas>
获取canvas,并获取其渲染上下文
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 获取宽高
const { width, height } = canvas;
1.1 替换内容
“您的浏览器不支持 Canvas!” 代表的是替换内容, 不支持<canvas>的浏览器,将会显示该内容。
1.2 渲染上下文
const ctx = canvas.getContext('2d'); // 二维上下文
const ctx = canvas.getContext('webgl'); // 三维上下文
1.3 坐标系
左上角为坐标原点,x轴向右,y轴向下。

二、形状绘制-矩形绘制
绘制一个填充的矩形,也就是一个实心的矩形:
ctx.fillRect(25, 25, 100, 100);
绘制一个矩形的边框,也就是一个空心的矩形:
ctx.strokeRect(50, 50, 50, 50);
清除指定矩形区域里内容:
ctx.clearRect(45, 45, 60, 60);
三个方法的参数都是 (x, y, width, height),并且这三个函数调用以后,会马上显示在canvas上,即时生效。
三、形状绘制-路径绘制
ctx.beginPath(); // 开始绘制路径
ctx.moveTo(x, y); // 将画笔移动到指定的坐标上
ctx.closePath(); // 闭合路径,从当前点到开始点闭合路径
ctx.stroke(); // 绘制图形轮廓,不会自动闭合。
ctx.fill(); // 填充路径的内容区域,生成实心图形。会自动闭合。
生成路径步骤:
- 开始绘制路径
beginPath(); - 调用函数,指定绘制路径(直线、弧线等);
- 闭合路径
closePath()、描边stroke()或填充路径fill()。
注意:
closePath()不是必须的。fill()对于没有闭合的路径,会自动闭合;stroke()不会自动闭合。moveTo()可以调用多次。
3.1 直线
ctx.lineTo(x, y); // 绘制一条从当前位置到(x,y)的直线
比如绘制一个三角形
ctx.beginPath();
ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.closePath();
ctx.stroke();
3.2 弧线
绘制圆弧路径。对于圆弧的弧度,x轴方向为 0,顺时针为正,逆时针为负。
/**
* 绘制圆弧路径
* @param {number} x 圆弧中心(圆心)的 x 轴坐标。
* @param {number} y 圆弧中心(圆心)的 y 轴坐标。
* @param {number} radius 圆弧的半径。
* @param {number} startAngle 圆弧的起始点, x轴方向开始计算,单位以弧度表示。
* @param {number} endAngle 圆弧的终点, 单位以弧度表示。
* @param {boolean} anticlockwise 如果为 true,逆时针绘制圆弧,反之,顺时针绘制。默认为顺时针。
*/
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
根据控制点和半径绘制圆弧路径
/**
* 根据控制点和半径绘制圆弧路径
* @param {number} x1 第一个控制点的 x 轴坐标。
* @param {number} y1 第一个控制点的 y 轴坐标。
* @param {number} x2 第二个控制点的 x 轴坐标。
* @param {number} y2 第二个控制点的 y 轴坐标。
* @param {number} radius 圆弧的半径。
*/
ctx.arcTo(x1, y1, x2, y2, radius);
根据当前描点与给定的控制点1连接的直线,和控制点1与控制点2连接的直线,作为使用指定半径的圆的切线,画出两条切线之间的弧线路径。
注意: 弧度=(Math.PI/180)*角度
3.3 贝塞尔曲线
二阶贝塞尔曲线
/**
* 绘制二次贝塞尔曲线
* @param {number} cp1x 控制点的 x 轴坐标。
* @param {number} cp1y 控制点的 y 轴坐标。
* @param {number} x 结束点的 x 轴坐标。
* @param {number} y 结束点的 y 轴坐标。
*/
ctx.quadraticCurveTo(cp1x, cp1y, x, y);
三阶贝塞尔曲线
/**
* 绘制三次贝塞尔曲线
* @param {*} cp1x 控制点1的 x 轴坐标。
* @param {*} cp1y 控制点1的 y 轴坐标。
* @param {*} cp2x 控制点2的 x 轴坐标。
* @param {*} cp2y 控制点2的 y 轴坐标。
* @param {*} x 结束点的 x 轴坐标。
* @param {*} y 结束点的 y 轴坐标。
*/
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);

3.4 矩形
和前面的绘制矩形不同,这个是将矩形的路径添加到当前的路径上。
ctx.rect(x, y, width, height);
注意:当该方法执行的时候,moveTo()方法自动设置坐标参数(0,0)。也就是说,当前笔触自动重置回默认坐标。
四、样式和颜色
4.1 颜色 Color
设置图形的填充颜色
ctx.fillStyle = color;
设置图形轮廓的颜色
ctx.strokeStyle = color;
颜色可以设置为符合 CSS3颜色值标准 的有效字符串,比如 orange、#FFA500、rgb(255,165,0)、rgba(255,165,0,1)。
注意: 一旦设置了
strokeStyle或者fillStyle的值,那么这个新值就会成为新绘制的图形的默认值。如果要给每个图形上不同的颜色,需要重新设置fillStyle或strokeStyle的值。
4.2 透明度 Transparency
在设置颜色的时候,可以设置透明的颜色,比如
ctx.strokeStyle = "rgba(255,0,0,0.5)";
4.3 线型 Line Style
ctx.lineWidth = value; //设置线条宽度
ctx.lineCap = type; //设置线条末端样式
ctx.lineJoin = type; // 设定线条与线条间接合处的样式
ctx.setLineDash(segments); // 设置当前虚线样式
ctx.getLineDash(); // 返回一个包含当前虚线样式,长度为非负偶数的数组
ctx.lineDashOffset = value; //设置虚线样式的起始偏移量
4.3.1 线条宽度
宽度为1.0的线,边缘较为模糊的原因:
如果你想要绘制一条从 (3,1) 到 (3,5),宽度是 1.0 的线条,你会得到像第二幅图一样的结果。实际填充区域(深蓝色部分)仅仅延伸至路径两旁各一半像素。而这半个像素又会以近似的方式进行渲染,这意味着那些像素只是部分着色,结果就是以实际笔触颜色一半色调的颜色来填充整个区域(浅蓝和深蓝的部分)。这就是为何宽度为 1.0 的线并不准确的原因。

4.3.2 lineCap 线端样式
样式有:butt,round 和 square。默认是 butt。

4.3.3 lineJoin
样式有round, bevel 和 miter。默认是 miter。

4.3.4 虚线
参数 segments,表示一组描述交替绘制线段和间距(坐标空间单位)长度的数字。 如果数组元素的数量是奇数, 数组的元素会被复制并重复。
从上到下segments参数分别是:
ctx.setLineDash([]);
ctx.setLineDash([1, 1]);
ctx.setLineDash([10, 10]);
ctx.setLineDash([20, 5]);
ctx.setLineDash([15, 3, 3, 3]);
ctx.setLineDash([20, 3, 3, 3, 3, 3, 3, 3]);
ctx.setLineDash([12, 3, 3]); // Equals [12, 3, 3, 12, 3, 3]

4.4 阴影 Shadows
ctx.shadowOffsetX = float; // 阴影在 X 轴的延伸距离
ctx.shadowOffsetY = float; // 阴影在 Y 轴的延伸距离
ctx.shadowBlur = float; // 设定阴影的模糊程度
ctx.shadowColor = color; // 设定阴影颜色效果
注意:shadowOffsetX 和 shadowOffsetY,负值表示阴影会往上或左延伸,正值则表示会往下或右延伸
4.5 渐变 Gradients
渐变可以用来填充和描边。创建 CanvasGradient 对象,再赋值给fillStyle 或 strokeStyle 属性
- 创建 CanvasGradient 对象。
/**
* 线性渐变
* @param {*} x1 渐变起点 x
* @param {*} y1 渐变起点 y
* @param {*} x2 渐变终点 x
* @param {*} y2 渐变终点 y
*/
var lineargradient = ctx.createLinearGradient(x1, y1, x2, y2);
/**
* 放射性渐变
* @param {*} x0 开始圆形的 x 轴坐标。
* @param {*} y0 开始圆形的 y 轴坐标。
* @param {*} r0 开始圆形的半径。
* @param {*} x1 结束圆形的 x 轴坐标。
* @param {*} y1 结束圆形的 y 轴坐标。
* @param {*} r1 结束圆形的半径。
*/
var radialgradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
- 上色
/**
* 添加渐变颜色
* @param {*} position 渐变中颜色所在的相对位置, 0.0 ~ 1.0 之间的值。
* @param {*} color 有效的 CSS 颜色值
*/
gradient.addColorStop(position, color);
- 赋值给 fillStyle 或 strokeStyle
ctx.fillStyle = lineargradient;
ctx.strokeStyle = radialgradient;
4.6 图案样式 Patterns
使用指定的图像创建模式
/**
*
* @param {*} image 作为重复图像源的 CanvasImageSource 对象
* @param {*} type 指定如何重复图像: repeat,repeat-x,repeat-y, no-repeat
*/
ctx.createPattern(image, type)
使用方式:
// 创建新 image 对象,用作图案
var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
img.onload = function() {
// 创建图案
var ptrn = ctx.createPattern(img, 'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, 150, 150);
}
注意:与 drawImage 有点不同,createPattern 需要确认 image 对象已经 onload,否则图案可能效果不对的。
4.7 填充规则
- "nonzero",默认值
- "evenodd"
ctx.fill("evenodd");
五、文本绘制
/**
* 填充指定文本
* @param {number} text 文本
* @param {number} x
* @param {number} y
* @param {number} maxWidth
*/
ctx.fillText(text, x, y, maxWidth);
/**
* 绘制指定文本边框
* @param {number} text 文本
* @param {number} x
* @param {number} y
* @param {number} maxWidth
*/
ctx.strokeText(text, x, y, maxWidth);
5.1 文本样式
ctx.font = value; // 字体样式,和 CSS font 属性相同的语语法
ctx.textAlign = value; // 文本对齐选项, 默认值是 start
ctx.textBaseline = value; // 基线对齐选项, 默认值是 alphabetic
ctx.direction = value; // 文本方向, ltr, rtl, inherit, 默认值是 inherit
5.1.1 textAlign
可选的值包括:start, end, left, right or center

5.1.2 textBaseline
可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。

5.2 测量文本宽度
测量出来的数据和文本样式有关
var text = ctx.measureText("foo"); // TextMetrics object
console.log(text.width);
六、图片绘制
6.1 图片来源
- HTMLImageElement。由
Image()构造出来的,或<img>元素。需要 onload 以后才能绘制。 - HTMLVideoElement。
<video>元素,从视频中抓取当前帧。即使视频是不可见的。 - HTMLCanvasElement。
<canvas>元素。 - ImageBitmap。高性能位图,可由上述元素或其他来源生成。
6.2 绘制方法
方式一
/**
* 绘制图片
* @param {*} image 图片源
* @param {*} x 在目标 canvas 里的起始坐标 x
* @param {*} y 在目标 canvas 里的起始坐标 y
*/
ctx.drawImage(image, x, y);
方式二,可以缩放图片的大小
/**
* 绘制图片
* @param {*} image 图片源
* @param {*} x 在目标 canvas 里的起始坐标 x
* @param {*} y 在目标 canvas 里的起始坐标 y
* @param {*} width 目标 canvas 画入时应该缩放的大小 width
* @param {*} height 目标 canvas 画入时应该缩放的大小 height
*/
ctx.drawImage(image, x, y, width, height);
方式三,可做切片显示
/**
* 绘制图片
* @param {*} image 图片源
* @param {*} sx image的矩形(裁剪)选择框的左上角 X 轴坐标。
* @param {*} sy image的矩形(裁剪)选择框的左上角 Y 轴坐标
* @param {*} sWidth image的矩形(裁剪)选择框的宽度
* @param {*} sHeight image的矩形(裁剪)选择框的高度
* @param {*} dx image的左上角在目标canvas上 X 轴坐标
* @param {*} dy image的左上角在目标canvas上 Y 轴坐标。
* @param {*} dWidth image在目标canvas上绘制的宽度
* @param {*} dHeight image在目标canvas上绘制的高度
*/
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
可以参考下图:

注意:图片跨域问题,需要使用
crossOrigin属性,并且图片服务器需要允许跨域。
七、Transformations(变形)
7.1 状态保存和恢复
ctx.save(); // 保存画布(canvas)的所有状态
ctx.restore(); // 恢复 canvas 状态
canvas 的状态就是当前应用的所有样式和形变的一个快照。每当 save 以后,当前状态就被推到栈中保存,调用restore,上一个保存的状态就栈中弹出。
可保存的状态有:
- 形变(移动、旋转、缩放)
- 裁剪路径
- 样式、颜色、字体等属性
7.2 移动 Translating
用来移动canvas的坐标原点。
ctx.translate(x, y);
7.3 旋转 Rotating
以原点为中心旋转 canvas
ctx.rotate(angle); // angle 顺时针方向的,以弧度为单位
7.4 缩放 Scaling
增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大
ctx.scale(x, y);
将坐标移动到 canvas 中心位置,并将y轴指向改为向上。
const { width, height } = canvas;
ctx.translate(0.5 * width, 0.5 * height);
ctx.scale(1, -1);
7.5 变形 Transforms
/**
* 缩放、旋转、移动和倾斜上下文
* @param {*} a (m11) 水平缩放。
* @param {*} b (m12) 垂直倾斜。
* @param {*} c (m21) 水平倾斜。
* @param {*} d (m22) 垂直缩放。
* @param {*} e (dx) 水平移动。
* @param {*} f (dy) 垂直移动。
*/
ctx.transform(a, b, c, d, e, f);
参数构成了该矩阵:
ctx.setTransform(a, b, c, d, e, f); //更改当前形变。
这个方法会将当前的变形矩阵重置为单位矩阵,然后用相同的参数调用 transform 方法。从根本上来说,该方法是取消了当前变形,然后设置为指定的变形,一步完成。
ctx.resetTransform(); // 重置当前形变。
重置当前变形为单位矩阵,它和调用以下语句是一样的:ctx.setTransform(1, 0, 0, 1, 0, 0);
八、合成与裁剪
8.1 合成
在已有图形上,再画新图形,使用不同的策略,会有不一样的效果。
ctx.globalCompositeOperation = type; // 修改遮盖策略,默认 source-over
共有12个参数,各个参数下的效果示例
8.2 裁剪路径
ctx.clip(); // 将当前的路径转换为裁剪路径
在路径以外的部分都不会在 canvas 上绘制出来
九、基本动画
9.1 动画的基本步骤:
- 清空 canvas。最简单的做法就是用 clearRect 方法。
- 保存 canvas 状态
- 绘制动画图形(animated shapes)
- 恢复 canvas 状态
9.2 操控动画
定时执行重绘的方法
setInterval(function, delay);
setTimeout(function, delay);
requestAnimationFrame(callback); // 推荐
十、像素操作
10.1 ImageData 对象
ImageData 对象中存储着 canvas 对象真实的像素数据。
// 只读属性:
width // 图片宽度,单位是像素
height // 图片高度,单位是像素
data // 数组,存储像素相信
data 包含的数据个数为: 高度 × 宽度 × 4
// 根据行列(从0开始),获取指定像素的R/G/B/A的值
imageData.data[((row * (imageData.width * 4)) + (col * 4)) + 0/1/2/3];
10.2 创建 ImageData 对象
创建空白的ImageData对象,所有像素被预设为透明黑,即每个像素的RGBA为(0,0,0,0)。
var myImageData = ctx.createImageData(width, height);
创建一个和anotherImageData对象相同像素的ImageData对象,所有像素被预设为透明黑。这个并非复制对象。
var myImageData = ctx.createImageData(anotherImageData);
10.3 获取场景像素数据
var myImageData = ctx.getImageData(left, top, width, height);
画布之外的元素会被设置成透明的黑。
10.4 更改场景中的像素数据
/**
* 更改场景中的像素数据
* @param { ImageData } imageData 包含像素值的数组对象
* @param {*} dx x 轴方向的偏移量
* @param {*} dy y 轴方向的偏移量
*/
ctx.putImageData(imageData, dx, dy);
10.4.1 图片灰度
var grayscale = function() {
for (var i = 0; i < imageData.length; i += 4) {
var avg = (imageData[i] + imageData[i +1] + imageData[i +2]) / 3;
imageData[i] = avg; // red
imageData[i + 1] = avg; // green
imageData[i + 2] = avg; // blue
}
ctx.putImageData(imageData, 0, 0);
};
10.4.2 图片反色
var invert = function() {
for (var i = 0; i < imageData.length; i += 4) {
imageData[i] = 255 - imageData[i]; // red
imageData[i + 1] = 255 - imageData[i + 1]; // green
imageData[i + 2] = 255 - imageData[i + 2]; // blue
}
ctx.putImageData(imageData, 0, 0);
};
10.5 反锯齿
zoomctx.imageSmoothingEnabled = value; // 图片是否平滑
true表示图片平滑(默认值),false表示图片不平滑
10.6 保存图片
生成 PNG 图片
canvas.toDataURL('image/png');
生成 JPG 图片
canvas.toDataURL('image/jpeg', quality); // quality, 0~1, 1表示最好品质
创建一个Blob对像
/**
* 创建一个Blob对像
* @param {function} callback 回调函数,可获得一个单独的Blob对象参数。
* @param {string} type 图片格式,默认格式为image/png。
* @param {number} encoderOptions 值在0与1之间。图片格式为image/jpeg或者image/webp时用来指定图片展示质量。
*/
canvas.toBlob(callback, type, encoderOptions)
十一、优化
- 在离屏canvas上预渲染相似的图形或重复的对象
- 避免浮点数的坐标点,用整数取而代之
- 不要在用drawImage时缩放图像
- 使用多层canvas去画一个复杂的场景
- 用CSS设置大的背景图
- 用CSS transforms特性缩放画布
- 关闭透明度
- 将画布的函数调用集合到一起(例如,画一条折线,而不要画多条分开的直线)
- 避免不必要的画布状态改变
- 渲染画布中的不同点,而非整个新状态
- 尽可能避免 shadowBlur特性
- 尽可能避免text rendering
- 尝试不同的方法来清除画布(clearRect() vs. fillRect() vs. 调整canvas大小)
- 有动画,请使用window.requestAnimationFrame() 而非window.setInterval()
- 请谨慎使用大型物理库