上篇主要是理論的概述,本篇會多些實踐,來說講canvas的基礎用法,幷包含一些基礎三角函數的應用,推薦沒有canvas基礎的朋友閱讀,熟悉的朋友能夠跳過。
本人能力有限,歡迎牛人共同討論,批評指正。javascript
canvas的API有不少,若是一一列舉30分鐘你是絕對看不完的,並且怎麼流水帳還不如本身去看文檔呢(笑),本教程的思路是用實例一步一步從無到有講解基礎用法。
canvas相關文檔html
<canvas>
標籤,添加canvas元素;<canvas>
標籤的id,得到canvas對象;<canvas id="canvas" width="400" height="400"></canvas>
const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d');
首先咱們來畫個紅邊黃底的箭頭,使用面向對象的代碼組織方式,所有代碼以下。
類名爲Arrow。它擁有x軸座標、y軸座標、底的顏色color、旋轉弧度rotation四個屬性。
實例方法是draw(),它須要一個context對象做爲參數,就是準備工做裏的context,它就至關因而畫筆,這裏實際上是相似依賴注入的過程,將canvas的畫筆交給實例的draw()方法,實例用這個畫筆去畫出箭頭,繪畫過程見代碼註釋。特別注意如下幾點:java
<canvas>
自己是透明的,可使用CSS給它個背景,例子中廣泛使用白色背景。/** * 箭頭類 * @class Representing a arrow. */ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "Arrow" }] */ class Arrow { /** * Create a arrow. */ constructor() { this.x = 0; this.y = 0; this.color = '#ffff00'; this.rotation = 0; } /** * Draw the arrow. * @param {Object} _context - The canvas context. */ draw(_context) { const context = _context; // 會先保存畫筆狀態 context.save(); // 移動畫筆 context.translate(this.x, this.y); // 旋轉畫筆 context.rotate(this.rotation); // 設置線條寬度 context.lineWidth = 2; // 設置線條顏色 context.strokeStyle = '#ff0000'; // 設置填充顏色 context.fillStyle = this.color; // 開始路徑 context.beginPath(); // 將筆移動到相對位置 context.moveTo(-50, -25); // 畫線到相對位置 context.lineTo(0, -25); context.lineTo(0, -50); context.lineTo(50, 0); context.lineTo(0, 50); context.lineTo(0, 25); context.lineTo(-50, 25); context.lineTo(-50, -25); // 閉合路徑 context.closePath(); // 填充路徑包圍區 context.fill(); // 繪製路徑 context.stroke(); // 載入保存的筆信息 context.restore(); } }
同理咱們還能夠畫點其餘的,好比一個圓ball.js,稍微多些參數,慢慢理解。
成品的效果能夠先看這個(稍微劇透):一個會跟蹤鼠標位置的箭頭git
如今咱們已經掌握了畫畫的基本功,而且能夠畫箭頭arrow.js和圓ball.js,然而這樣只是靜止畫,接下來咱們須要一個循環,不斷的執行擦除和重畫的工做才能實現幀動畫。
下面這段代碼的中繪圖函數drawFrame被當即執行,並遞歸調用自身,你將會在大部分例子中看到。
循環原理上一篇已經說明,再也不重複。這裏要說明的是clearRect(),這個函數接受一個矩形座標,也就是(x軸座標,y軸座標,矩形寬度,矩形高度),用於清除矩形區域內的畫。
例子裏直接是清除了整個畫布,但這不是絕對的,刷不刷新,是局部刷新仍是所有刷新,都須要靈活處理。
這裏有個不刷新的例子:鼠標畫圖工具github
(function drawFrame() { // 相似setTimeout的操做 window.requestAnimationFrame(drawFrame, canvas); // 將畫布擦乾淨 context.clearRect(0, 0, canvas.width, canvas.height); // ...繼續你的做畫 }());
如今畫面已是在不斷的重繪,但爲何仍是靜止的呢?由於每一次刷新都沒有改變要畫的內容。
那咱們就給它一個目標吧,這樣它才能動起來,好比就讓箭頭始終指向鼠標。
下面是核心代碼,主要目的就是求出每幀arrow的旋轉角度,這裏使用的工具類mouse會實時返回鼠標的x,y軸座標,封裝原理上一篇已經講過,根據這鼠標的座標和arrow的座標,便可獲得鼠標的相對於arrow的距離dx和dy,以下圖: canvas
而arrow的旋轉角度便可以經過dx和dy使用反正切函數獲得,這裏須要注意幾點:app
完整實例:一個會跟蹤鼠標位置的箭頭dom
window.onload = function () { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); const mouse = utils.captureMouse(canvas); const arrow = new Arrow(); arrow.x = canvas.width / 2; arrow.y = canvas.height / 2; (function drawFrame() { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); const dx = mouse.x - arrow.x; const dy = mouse.y - arrow.y; arrow.rotation = Math.atan2(dy, dx); arrow.draw(context); }()); };
終於順利過渡到三角函數的話題(笑)。三角函數不止有反正切一個應用,下面再看一個例子。
下面是一個ball在上下運動的核心代碼,重點就是ball的y軸座標改變,就是這句:函數
ball.y = clientY + Math.sin(angle) * range;
利用Math.sin(angle)的取值範圍是-1到1,而且會隨着angle增大而反覆,使ball在必定範圍上下運動。
完整例子:一個上下運動的球(可調參數版)工具
window.onload = function () { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); const ball = new Ball(); let angle = 0; // 運動中心 const clientY = 200; // 範圍 const range = 50; // 速度 const speed = 0.05; ball.x = canvas.width / 2; ball.y = canvas.height / 2; (function drawFrame() { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); ball.y = clientY + Math.sin(angle) * range; angle += speed; ball.draw(context); }()); };
只是上下運動不過癮,那就讓圓前進吧,其實就是每幀改變x軸的位置。
核心代碼以下,相比前面的上下運動,多了x軸的速度,每幀移動一點就造成了波浪前進的效果。
完整實例:一個波浪運動的球
window.onload = function () { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); const ball = new Ball(); let angle = 0; const centerY = 200; const range = 50; const xspeed = 1; const yspeed = 0.05; ball.x = 0; (function drawFrame() { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); ball.x += xspeed; ball.y = centerY + Math.sin(angle) * range; angle += yspeed; ball.draw(context); }()); };
其餘的應用就不一一講解,羅列出來一些: