数据可视化之路 - canvas 入门

Canvas API 提供了经过 JavaScript 绘制图形的能力 。它普遍用于动画、游戏图形、数据可视化、照片处理和实时视频处理领域。本文将会简单介绍下 canvas 。文章的大部份内容来源于 MDN 的 canvas 教程,想深刻了解 canvas 的能够去看一下。javascript

初识 canvas

// 栗子 1
<canvas id="tutorial" width="150" height="150"></canvas>
复制代码

这就是一个 canvas 标签,看起来和普通的 html 标签没什么不一样,重要的经过 canvas 标签咱们能够获取其渲染上下文,canvas 的全部API都经过这个渲染上下文暴露出来,如下讲解的 API 也都基于此上下文。html

canvas 翻译过来是画布的意思,因此接下来咱们会对照画布来介绍 canvas 的API。java

var canvas = document.getElementById("tutorial");
var ctx = canvas.getContext("2d");
复制代码

绘制图形

image.png

canvas 坐标系统中,元素的左上角为坐标原点,向右下延伸坐标轴,全部图形相对于原点绘制,默认 1 网格单位对应 1 像素,好比栗子1 中的 canvas 元素就造成一个 150X150 的坐标系。 上图中的矩形坐标即为 (x, y)。
和 svg 不一样,canvas 只提供了一种基本图形:矩形,不过借助路径咱们能够绘制想要的任意图形。 git

绘制矩形

canvas 提供了三种矩形 API,咱们能够经过渲染上下文获取到它们。全部 API 接受四个参数: x, y, width, height。github

function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
  }
}
复制代码

fillRect 和 strokeRect 分别会 “填充” 和 “描边“ 一片矩形区域。调用它们会当即绘制到画布上。
clearRect 会像橡皮擦同样清除特定的矩形区域。
结果如图:编程

image.png

绘制路径

路径由一系列的点组成,经过路径的组合咱们能够绘制须要的任何图形。绘制一条路径步骤以下:canvas

  1. 经过 beginPath  建立路径,并建立一个路径缓存区,当前坐标为起点。
  2. 而后经过 绘制命令 绘制路径,将绘制的 直线、弧线等存入缓存区,从而组合成图形。
  3. 最后 closePath 会在当前坐标和起点连起一条直线(可选), 最后能够 填充 或 描边 路径。

这个绘制步骤和画画差很少,落笔、描边到填充和真实世界的心智模型是相同的。其中的重头戏就是步骤2的绘制命令:
lineTo 、 arc 、[bezierCurveTo()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo)等命令分别绘制直线、弧线和贝塞尔曲线路径。而 moveTo  命令接受 x、y 两个参数,调用其会将画笔移动到指定的 x,y 坐标。
好比这里有个稍微复杂的栗子,绘制一个吃豆人缓存

路径复用

在吃豆人的栗子里,咱们反复调用 roundedRect 函数来生成圆角矩形, canvas 提供了路径复用的方法,这就是 Path2D,经过 Path2d 咱们能够建立路径对象,调用绘制命令、甚至合并两个路径对象。以吃豆人中的圆角矩形函数为栗,使用Path2D 后的代码:dom

// A utility function to draw a rectangle with rounded corners.
function roundedRect(ctx, x, y, width, height, radius) {
  const path = new Path2D();
  path.moveTo(x, y + radius);
  path.lineTo(x, y + height - radius);
  path.arcTo(x, y + height, x + radius, y + height, radius);
  path.lineTo(x + width - radius, y + height);
  path.arcTo(x + width, y + height, x + width, y + height - radius, radius);
  path.lineTo(x + width, y + radius);
  path.arcTo(x + width, y, x + width - radius, y, radius);
  path.lineTo(x + radius, y);
  path.arcTo(x, y, x, y + radius, radius);

  ctx.stroke(path);
}
复制代码

加点样式?

canvas 经过状态机来保持 画笔 和 画布的状态,咱们能够经过 API 来改变画笔的状态来绘制不一样样式的图形。好比经过 fillStyle 和 strokeStyle 改变填充和描边的样式,经过 lineWidth 改变画笔的尺寸等。经过 createLinearGradient 、[createPattern(image, type)](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createPattern)来绘制渐变色和图片背景,这里再也不赘述。svg

文本绘制

去看原文吧

坐标轴变换

去看原文吧

加点动画?

canvas 动画的限制在于:图形绘制结束后没法再更改图形的状态(尺寸、颜色、坐标等)。这和咱们熟悉的 html 元素不同,dom 元素在渲染后还能够进行作位移、缩放等动画。因为以上缘由,在 canvas 中作动画须要遵循如下步骤:

  1. 使用 clearRect 等方法清除画布
  2. 保存 canvas 的初始状态
  3. 绘制当前动画帧,这一步你可能会修改 canvas 状态
  4. 恢复 canvas 初始状态。

这里有一个时钟动画的栗子,咱们将不一样的图形封装为对象,绘制图形时只须要调用对象的 draw 方法,这样统一了编程模型。

class Hour {
  constructor(ctx) {
    this.ctx = ctx;
  }
  draw() {
    this.ctx.lineWidth = 14;
    this.ctx.beginPath();
    this.ctx.moveTo(-20, 0);
    this.ctx.lineTo(80, 0);
    this.ctx.stroke();
  }
}
复制代码

绘制的每一帧咱们会不断更新画布的状态,为了避免污染下一帧的画布状态,咱们须要不断调用 save  和 restore 来保存和回退画布状态。

这一章简单介绍了 canvas的基本知识,下篇文章会涉及 canvas 内的事件处理、碰撞检测等内容,并利用这些实现一些炫酷的效果,敬请期待。


本人正在编写数据可视化之路系列文章,输出一些可视化方面的教程和经验,你能够经过如下途径阅读它们。

相关文章
相关标签/搜索