Canvas 入門指南

mdn 文檔javascript

canvas 能夠用於動畫、遊戲畫面、數據可視化、圖片編輯以及實時視頻處理等內容。html

canvas api 主要聚焦於 2D 圖形。java

WebGL api 主要聚焦於硬件加速的 2D 和 3D 圖形。canvas

先來看一個基礎事例:api

<canvas id="canvas">
	Your browser not support canvas, please update browser.
</canvas>

<script> function draw() { // 獲取 canvas 元素 const canvas = document.getElementById('canvas'); // 建立畫板 const context = canvas.getContext('2d'); // 填充形狀 context.fillRect(10, 10, 55, 50); // 填充顏色 context.fillStyle = 'black'; } draw(); </script>
複製代碼

這時,瀏覽器會出現一個黑色的方塊:瀏覽器

CleanShot 2021-05-17 at 10.55.23@2x.png

繪製圖形

首先學習如何繪製矩形。緩存

繪製矩形

canvas 提供了 3 種方法來繪製矩形:微信

  1. fillRect(x, y, width, height): 繪製了一個填充的矩形;
  2. strokeRect(x, y, width, height): 繪製了一個矩形的邊框;
  3. clearRect(x, y, width, height): 清楚指定矩形區域,讓清楚部分徹底透明;
  4. rect(x, y, width, height): 矩形路徑

使用以上三個方法構建一個空心矩形:markdown

function draw() {
	const canvas = document.getElementById('canvas');
	const context = canvas.getContext('2d');

	context.fillRect(25, 25, 100, 100);
	context.clearRect(50, 50, 50, 50);
}

draw();
複製代碼

如圖下所示:svg

CleanShot 2021-05-17 at 11.21.35@2x.png

繪製路徑

繪圖的基本元素是路徑。

路徑:經過不一樣顏色和寬度的線段或曲線相連造成的不一樣形狀的點的集合

一下是所須要的函數:

  1. beginPath(): 新建一條路徑,生成以後,圖形繪製命令被指向到路徑上生成路徑;
  2. closePath(): 閉合路徑以後圖形繪製命令又從新指向到上下文中;
  3. stroke(): 經過線條來繪製圖形輪廓;
  4. fill(): 經過填充路徑的內容區域生成實心的圖形了;

用以上的方法來構建一個矩形:

function draw() {
	const canvas = document.getElementById('canvas');
	const context = canvas.getContext('2d');

	context.beginPath();
	context.moveTo(100, 100);
	context.lineTo(100, 0);
	context.lineTo(0, 0);
	context.lineTo(0, 100);
	context.fill();
}

draw();
複製代碼

生成路徑:

  1. beginPath(): 本質上,路徑是由不少子路徑構成,這些子路徑都是在一個列表中,全部的子路徑來構成圖形。每次調用這個方法,列表都會清空,而後就能夠從新繪製新的圖形了;

  2. moveTo(): 設置路徑以後,指定起始位置;

  3. lineTo(): 調用函數繪製路徑;

  4. closePath(): 閉合路徑(非必需),在調用 fill() 函數時,全部沒有閉合的形狀都會自動閉合。

移動筆

moveTo(): 從畫布上的一個點移動到另一個點。

繪製直線

使用 lineTo(x, y) 方法:從當前位置到 (x, y) 的指導位置的線段。

繪製圓弧

  1. arc(x, y, radius, startAngle, endAngle, anticlockwise): 畫一個以 (x, y) 爲圓心的以 radius 爲半徑的圓弧/園。從 startAngle 開始,到 endAngle 結束,按照 anticlockwise 給定的方向。
  2. arcTo(x1, y1, x2, y2, radius): 根據給定的控制點和半徑畫一段圓弧,再以直線連接兩個控制點。

arc() 函數中,表示角度的單位是弧度,不是角度。

角度轉弧度的公式爲:弧度 = 角度 * (π/ 180)

使用以上方法,畫一個實心圓:

function draw() {
	const canvas = document.getElementById('canvas');
	const context = canvas.getContext('2d');

	context.beginPath();
	context.moveTo(50, 50);

	context.arc(50, 50, 25, 0, 2 * Math.PI, true);
	context.fill();
}

draw();
複製代碼

如圖下所示:

CleanShot 2021-05-17 at 11.50.08@2x.png

Path2D

爲了簡化代碼以及提升性能,咱們可使用 Path2D 用來緩存或者記錄繪畫命令。

來實驗一下,來畫一個內切圓:

function draw() {
	const canvas = document.getElementById('canvas');
	const context = canvas.getContext('2d');

	const rectangle = new Path2D();
	rectangle.rect(0, 0, 100, 100);

	const circle = new Path2D();
	circle.moveTo(50, 50);
	circle.arc(50, 50, 50, 2 * Math.PI, false);

	context.stroke(rectangle);
	context.stroke(circle);
}

draw();
複製代碼

CleanShot 2021-05-17 at 12.09.55@2x.png

SVG Path

可使用 svg path data 來初始化 canvas 上面的路徑

到如今爲止,咱們可使用一系列路徑來將咱們所須要的圖形,繪畫在畫布上面了。大致上的步驟以下:

  1. 建立畫布
  2. 開始路徑
  3. 設置初始點
  4. 繪畫路徑
  5. 閉合路徑

示例

接下來,就經過上面學習到的知識來畫一個簡單的柱狀圖:

軸線

生成軸線:

// 座標軸生成
createAxle(type = 'x') {
  const {point, axleLength} = this.defaults;

  const axle = new Path2D();
  axle.moveTo(...point);

  if (type === 'x') {
    axle.lineTo(point[0] + axleLength, point[1]);
  } else {
    axle.lineTo(point[0], point[1] - axleLength);
  }

  this.context.stroke(axle);
}
複製代碼

刻度

生成刻度:

// 生成刻度
createScale(type = 'x') {
  const {point, count, axleLength, scaleLength} = this.defaults;
  const ruleWidth = parseInt(axleLength / count, 10);

  const scale = new Path2D();

  for (let i = 0; i <= axleLength; i += ruleWidth) {
    if (type === 'x') {
      scale.moveTo(point[0] + i, point[1]);
      scale.lineTo(point[0] + i, point[1] + scaleLength);
    } else {
      scale.moveTo(point[0], point[1] - i);
      scale.lineTo(point[0] - scaleLength, point[1] - i);
    }
  }

  this.context.stroke(scale);
}
複製代碼

畫出柱狀圖

最後一步,咱們就要畫出柱狀圖的關鍵,柱子了。

// 畫柱狀圖
createRect() {
  const {point, axleLength, count} = this.defaults;
  const rectLength = parseInt(axleLength / count, 10);

  const rect = new Path2D();
  for (let i = 0; i <= count; i += 1) {
    rect.rect(
      point[0] + (rectLength * i),
      point[1],
      rectLength,
      -rectLength * i
    );
  }

  this.context.fill(rect);
}
複製代碼

完整代碼

class LineChart {
	constructor(config) {
		const canvas = document.getElementById('canvas');
		this.context = canvas.getContext('2d');

		this.defaults = {
			point: [50, 120],
			axleLength: 100,
			count: 5,
			scaleLength: 5
		};

		Object.assign(this.defaults, config);
	}

	// 座標軸生成
	createAxle(type = 'x') {
		const {point, axleLength} = this.defaults;

		const axle = new Path2D();
		axle.moveTo(...point);

		if (type === 'x') {
			axle.lineTo(point[0] + axleLength, point[1]);
		} else {
			axle.lineTo(point[0], point[1] - axleLength);
		}

		this.context.stroke(axle);
	}

	// 座標標尺
	createScale(type = 'x') {
		const {point, count, axleLength, scaleLength} = this.defaults;
		const ruleWidth = parseInt(axleLength / count, 10);

		const scale = new Path2D();

		for (let i = 0; i <= axleLength; i += ruleWidth) {
			if (type === 'x') {
				scale.moveTo(point[0] + i, point[1]);
				scale.lineTo(point[0] + i, point[1] + scaleLength);
			} else {
				scale.moveTo(point[0], point[1] - i);
				scale.lineTo(point[0] - scaleLength, point[1] - i);
			}
		}

		this.context.stroke(scale);
	}

	// 畫柱狀圖
	createRect() {
		const {point, axleLength, count} = this.defaults;
		const rectLength = parseInt(axleLength / count, 10);

		const rect = new Path2D();
		for (let i = 0; i <= count; i += 1) {
			rect.rect(
				point[0] + (rectLength * i),
				point[1],
				rectLength,
				-rectLength * i
			);
		}

		this.context.fill(rect);
	}

	draw() {
		this.createAxle('x');
		this.createAxle('y');

		this.createScale('x');
		this.createScale('y');

		this.createRect();
	}
}

const lineChart = new LineChart();

lineChart.draw();

複製代碼

結果以下所示:

CleanShot 2021-05-17 at 14.42.14@2x.png

剩下的工做還有一些,以後在更新吧!

  • 美化這個柱狀圖啊,如今這個太黑了
  • 標上刻度
  • 根據數據來對柱子進行調節


PS:你們看了後以爲對本身有幫助能夠三連留言,歡迎提出寶貴意見,也歡迎各位對相關技術有興趣的開發者(由團隊開發人員微信號x118422邀請)入羣,在線解答討論數據可視化、優化圖表性能等方面的技術問題~

相關文章
相關標籤/搜索