數據可視化之路 - 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 內的事件處理、碰撞檢測等內容,並利用這些實現一些炫酷的效果,敬請期待。


本人正在編寫數據可視化之路系列文章,輸出一些可視化方面的教程和經驗,你能夠經過如下途徑閱讀它們。

相關文章
相關標籤/搜索