[杂七杂八的学习记录] canvas 入门 & chart.js

435 阅读5分钟

canvas

<canvas> 元素可被用来通过 JavaScript(Canvas API 或 WebGL API)绘制图形及图形动画。

Canvas Api (2D API)

Canvas 是一个 2D 绘图 API。

canvas 渲染方式

  1. canvas 本质就是位图,而 dom 需要经过 layout、paint、光栅化等阶段才会被转化成位图;

  2. dom 的渲染模式称为驻留模式(Retained Mode),将 dom 元素对象放在内存中,最后调用系统的绘制 api 将中间产物绘制到屏幕;

    image.png

  3. canvas 的渲染模式称为快速模式(Immediate Mode),每次重绘都会直接基于 canvas 代码,不存在解析过程;

    image.png

应用优势

  1. 高性能:直接基于位图重绘、渲染快,内存消耗不受元素数量影响(dom 每个元素都是对象、都需要消耗内存);
  2. 灵活性:可以通过代码控制如何、何时绘制;

其他绘制方式

  1. svg:svg 是一个绘制形状的矢量 api(是使用 XML 来描述二维图形和绘图程序的语言),每个形状都有一个可以附加事件处理程序的对象。svg 在放大时会保持平滑,而 canvas 会变得像素化
  2. css: css 用于样式化 dom 元素,canvas 中没有 dom 元素;
  3. dom 动画:使用 css 或 ja 移动对象,在某些情况下会比 canvas 更流程;

适用场景

  1. svg:渲染现有形状到屏幕,例如来自 Adobe Illustrator 的插画;
  2. css/dom 动画:为较大的静态区域设置动画、3d 变换;
  3. canvas:图表、图形、动态图表、视频游戏;

绘图

HTMLCanvasElement.getContext(contextType, contextAttributes) 方法返回 canvas 的上下文, 2D 绘图 contextType 为 ‘2d’;

简单绘图

  1. ctx.fillStyle; // 设置填充样式,可为颜色、渐变对象或图像
  2. ctx.fillRect(x, y, width, height);

路径

  1. ctx.beginPath();
  2. ctx.moveTo(x, y); // 移动路径起始点
  3. ctx.lineTo(x, y);
  4. ctx.closePath();
  5. ctx.fill();
  6. ctx.strokeStyle; // 设置绘制路径样式
  7. ctx.lineWidth;
  8. ctx.stroke([path]); // 绘制当前路径
  9. ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); // 以贝塞尔曲线连接
  10. ctx.rect(x, y, width, height); // 创建矩形路径
  11. ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise); // 绘制圆弧路径, x, y 为圆弧圆心
  12. ctx.arcTo(x1, y1, x2, y2, radius); // 绘制圆弧路径, x1, y1 为起始点坐标, x2, y2 为终点坐标

坐标系

  1. 画布原点位于左上角,y 轴向下,x 轴向右;
  2. 如果在(5, 0) 位置绘制一条 1 像素宽的垂直线,它实际上会在 4.5 到 5.5 之间(可以通过偶数像素宽避免);

图片

  1. ctx.drawImage(img, dx, dy); // 正常绘图,dx, dy 指图片左上角在目标 canvas 中的坐标
  2. ctx.drawImage(img, dx, dy, dWidth, dHeight); // 缩放绘图,dWidth, dHeight 指图片在目标 canvas 中的宽、高
  3. ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); //裁剪绘图,sx, sy, sWidth, sHeight 指在 img 中的选择区域坐标和大小
  4. drawImage 方法在绘制时使用源元素的 CSS 大小(指 naturalWidth 和 naturalHeight,而不是 element.width 和 element.height);
  5. 图像插值通常比其他缩放方式快得多;

文本

  1. ctx.font; // 符合 css font 语法的字符串
  2. ctx.fillText(string, x, y); // x, y 是基线位置而不是顶部位置
  3. ctx.strokeText(string, x, y); // 绘制空心字体

渐变

  1. grad = ctx.createLinearGradient(x0, y0, x1, y1); // 渐变的起始点和结束点

  2. grad.addColorStop(offset, color);

    image.png

图片填充

  1. pattern = ctx.createPattern(img, repetition);
  2. img 可为 HTMLImageElement, HTMLImageElement, HTMLCanvasElement, CanvasRenderingContext2D, ImageBitmap, ImageData, Blob;
  3. repetition 可为 'repeat', 'repeat-x', 'repeat-y', 'no-repeat';
  4. 仅当图片已经加载时才可以使用图像纹理进行填充,所以可以从图像的 onload 回调中进行绘制;

保存图像

  1. ctx.getImageData(sx, sy, sw, sh); // 返回 ImageData 对象,用来描述 canvas 区域隐含的像素数据,ImageData 对象上可以获得 data(元素为 0~255 的一维数组,以 RGBA 顺序描述图像),height,width
  2. ctx.toBlob(callback, type, quality); // 将图片转为 blob 文件(之后可保存、或转为 url 后作为 img 标签的图像源),callback 为参数为 blob 的回调函数,type 为文件类型(image/png, image/jpg, image/webp),quality 为图片存为 jpeg 或 webp 格式时指定的图片展示质量
  3. ctx.toDataURL(type, encoderOptions); // 包含图片展示的 data URI(data:[<mediatype>][;base64],<data>)

不透明度

  1. ctx.globalAlpha; // 不透明度设置为 0 - 1 之间的分数
  2. 此不透明度设置适用于所有绘图操作

变换

  1. ctx.translate(x, y); // 将 canvas 的坐标按原始的 x 水平方向、y 垂直方向进行平移变换
  2. ctx.rotate(angle); // 坐标顺时针旋转弧度
  3. ctx.scale(x, y); // 坐标长度缩放
  4. ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复

状态保存

  1. ctx.save(); // 保存状态
  2. ctx.restore(); // 恢复到上次保存状态

剪裁

  1. ctx.clip([path], fillRule); // 根据当前路径裁剪,fillRule 可为 'nonzero' 和 'evenodd';
  2. nonzero:选定点向任意方向发出一条射线,考虑相交的路径方向和射线方向差,顺时针加 1,逆时针减 1,如果结果为 0 则选定点在路径外部,否则为内部;
  3. evenodd:选定点向任意方向发出一条射线,考虑相交的路径的条数,偶数则选定点在路径外部,否则为内部;

事件

  1. isPointInPath() //
  2. 浏览器无法感知 canvas 内部元素,所以需要将 canvas 整体的鼠标事件转换为内部事件;

canvas动画

  1. 用 requestAnimationFrame 配合动画;
  2. ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除背景

WebGL Api(3D api)

developer.mozilla.org/zh-CN/docs/…

chart.js

Scale:绘制图表基本部分

  1. core.scale.js 为主入口文件,Scale 类上实现 draw 方法,包扩绘制图表背景、网格线、边框、标题和标签;

    image.png

  2. drawGrid 为绘制网格线的方法,计算(_computeGridLineItems)每一条网格线位置和样式后,绘制直线。在绘制前后会 save() & restore(), 使画布恢复初始状态;

    image.png

  3. drawGrid 包括了两种网格线,在轴外侧和在图表区域内;

    image.png

    image.png

  4. 计算网格线位置,首先计算非平移方向的坐标;

    image.png

  5. 计算平移方向的坐标;

    image.png

    image.png

Element:绘制图表数据部分

  1. element.bar.js: 绘制条形图;

  2. draw: 绘制矩形,border 部分实际也是用 fill 填充路径(而不是 stroke);

    image.png

  3. inflateRect: 计算膨胀后(inflateAmount 不为 0)的条形图,borderWidth 表现为边框向内扩展,inflateAmount 表现为边框向外扩展;

    image.png