canvas
<canvas>元素可被用来通过 JavaScript(Canvas API 或 WebGL API)绘制图形及图形动画。
Canvas Api (2D API)
Canvas 是一个 2D 绘图 API。
canvas 渲染方式
-
canvas 本质就是位图,而 dom 需要经过 layout、paint、光栅化等阶段才会被转化成位图;
-
dom 的渲染模式称为驻留模式(Retained Mode),将 dom 元素对象放在内存中,最后调用系统的绘制 api 将中间产物绘制到屏幕;
-
canvas 的渲染模式称为快速模式(Immediate Mode),每次重绘都会直接基于 canvas 代码,不存在解析过程;
应用优势
- 高性能:直接基于位图重绘、渲染快,内存消耗不受元素数量影响(dom 每个元素都是对象、都需要消耗内存);
- 灵活性:可以通过代码控制如何、何时绘制;
其他绘制方式
- svg:svg 是一个绘制形状的矢量 api(是使用 XML 来描述二维图形和绘图程序的语言),每个形状都有一个可以附加事件处理程序的对象。svg 在放大时会保持平滑,而 canvas 会变得像素化;
- css: css 用于样式化 dom 元素,canvas 中没有 dom 元素;
- dom 动画:使用 css 或 ja 移动对象,在某些情况下会比 canvas 更流程;
适用场景
- svg:渲染现有形状到屏幕,例如来自 Adobe Illustrator 的插画;
- css/dom 动画:为较大的静态区域设置动画、3d 变换;
- canvas:图表、图形、动态图表、视频游戏;
绘图
HTMLCanvasElement.getContext(contextType, contextAttributes) 方法返回 canvas 的上下文, 2D 绘图 contextType 为 ‘2d’;
简单绘图
- ctx.fillStyle; // 设置填充样式,可为颜色、渐变对象或图像
- ctx.fillRect(x, y, width, height);
路径
- ctx.beginPath();
- ctx.moveTo(x, y); // 移动路径起始点
- ctx.lineTo(x, y);
- ctx.closePath();
- ctx.fill();
- ctx.strokeStyle; // 设置绘制路径样式
- ctx.lineWidth;
- ctx.stroke([path]); // 绘制当前路径
- ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); // 以贝塞尔曲线连接
- ctx.rect(x, y, width, height); // 创建矩形路径
- ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise); // 绘制圆弧路径, x, y 为圆弧圆心
- ctx.arcTo(x1, y1, x2, y2, radius); // 绘制圆弧路径, x1, y1 为起始点坐标, x2, y2 为终点坐标
坐标系
- 画布原点位于左上角,y 轴向下,x 轴向右;
- 如果在(5, 0) 位置绘制一条 1 像素宽的垂直线,它实际上会在 4.5 到 5.5 之间(可以通过偶数像素宽避免);
图片
- ctx.drawImage(img, dx, dy); // 正常绘图,dx, dy 指图片左上角在目标 canvas 中的坐标
- ctx.drawImage(img, dx, dy, dWidth, dHeight); // 缩放绘图,dWidth, dHeight 指图片在目标 canvas 中的宽、高
- ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); //裁剪绘图,sx, sy, sWidth, sHeight 指在 img 中的选择区域坐标和大小
- drawImage 方法在绘制时使用源元素的 CSS 大小(指 naturalWidth 和 naturalHeight,而不是 element.width 和 element.height);
- 图像插值通常比其他缩放方式快得多;
文本
- ctx.font; // 符合 css font 语法的字符串
- ctx.fillText(string, x, y); // x, y 是基线位置而不是顶部位置
- ctx.strokeText(string, x, y); // 绘制空心字体
渐变
-
grad = ctx.createLinearGradient(x0, y0, x1, y1); // 渐变的起始点和结束点
-
grad.addColorStop(offset, color);
图片填充
- pattern = ctx.createPattern(img, repetition);
- img 可为 HTMLImageElement, HTMLImageElement, HTMLCanvasElement, CanvasRenderingContext2D, ImageBitmap, ImageData, Blob;
- repetition 可为 'repeat', 'repeat-x', 'repeat-y', 'no-repeat';
- 仅当图片已经加载时才可以使用图像纹理进行填充,所以可以从图像的 onload 回调中进行绘制;
保存图像
- ctx.getImageData(sx, sy, sw, sh); // 返回 ImageData 对象,用来描述 canvas 区域隐含的像素数据,ImageData 对象上可以获得 data(元素为 0~255 的一维数组,以 RGBA 顺序描述图像),height,width
- ctx.toBlob(callback, type, quality); // 将图片转为 blob 文件(之后可保存、或转为 url 后作为 img 标签的图像源),callback 为参数为 blob 的回调函数,type 为文件类型(image/png, image/jpg, image/webp),quality 为图片存为 jpeg 或 webp 格式时指定的图片展示质量
- ctx.toDataURL(type, encoderOptions); // 包含图片展示的 data URI(data:[<mediatype>][;base64],<data>)
不透明度
- ctx.globalAlpha; // 不透明度设置为 0 - 1 之间的分数
- 此不透明度设置适用于所有绘图操作
变换
- ctx.translate(x, y); // 将 canvas 的坐标按原始的 x 水平方向、y 垂直方向进行平移变换
- ctx.rotate(angle); // 坐标顺时针旋转弧度
- ctx.scale(x, y); // 坐标长度缩放
- ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复
状态保存
- ctx.save(); // 保存状态
- ctx.restore(); // 恢复到上次保存状态
剪裁
- ctx.clip([path], fillRule); // 根据当前路径裁剪,fillRule 可为 'nonzero' 和 'evenodd';
- nonzero:选定点向任意方向发出一条射线,考虑相交的路径方向和射线方向差,顺时针加 1,逆时针减 1,如果结果为 0 则选定点在路径外部,否则为内部;
- evenodd:选定点向任意方向发出一条射线,考虑相交的路径的条数,偶数则选定点在路径外部,否则为内部;
事件
- isPointInPath() //
- 浏览器无法感知 canvas 内部元素,所以需要将 canvas 整体的鼠标事件转换为内部事件;
canvas动画
- 用 requestAnimationFrame 配合动画;
- ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除背景
WebGL Api(3D api)
developer.mozilla.org/zh-CN/docs/…
chart.js
Scale:绘制图表基本部分
-
core.scale.js 为主入口文件,Scale 类上实现 draw 方法,包扩绘制图表背景、网格线、边框、标题和标签;
-
drawGrid 为绘制网格线的方法,计算(_computeGridLineItems)每一条网格线位置和样式后,绘制直线。在绘制前后会 save() & restore(), 使画布恢复初始状态;
-
drawGrid 包括了两种网格线,在轴外侧和在图表区域内;
-
计算网格线位置,首先计算非平移方向的坐标;
-
计算平移方向的坐标;
Element:绘制图表数据部分
-
element.bar.js: 绘制条形图;
-
draw: 绘制矩形,border 部分实际也是用 fill 填充路径(而不是 stroke);
-
inflateRect: 计算膨胀后(inflateAmount 不为 0)的条形图,borderWidth 表现为边框向内扩展,inflateAmount 表现为边框向外扩展;