摘要:前言上篇主要是理論的概述,本篇會(huì)多些實(shí)踐,來講講的基礎(chǔ)用法,并包含一些基礎(chǔ)三角函數(shù)的應(yīng)用,推薦沒有基礎(chǔ)的朋友閱讀,熟悉的朋友可以跳過。完整實(shí)例一個(gè)會(huì)跟蹤鼠標(biāo)位置的箭頭三角函數(shù)上下運(yùn)動(dòng)終于順利過渡到三角函數(shù)的話題笑。
前言
上篇主要是理論的概述,本篇會(huì)多些實(shí)踐,來講講canvas的基礎(chǔ)用法,并包含一些基礎(chǔ)三角函數(shù)的應(yīng)用,推薦沒有canvas基礎(chǔ)的朋友閱讀,熟悉的朋友可以跳過。
本人能力有限,歡迎牛人共同討論,批評指正。
canvas的API有很多,如果一一列舉30分鐘你是絕對看不完的,而且怎么流水賬還不如自己去看文檔呢(笑),本教程的思路是用實(shí)例一步一步從無到有講解基礎(chǔ)用法。
canvas相關(guān)文檔
布置畫布:通過添加標(biāo)簽,添加canvas元素;
獲取畫布:通過標(biāo)簽的id,獲得canvas對象;
獲得畫筆:通過canvas對象的getContext("2d")方法,獲得2D環(huán)境。
const canvas = document.getElementById("canvas"); const context = canvas.getContext("2d");畫個(gè)箭頭
首先我們來畫個(gè)紅邊黃底的箭頭,使用面向?qū)ο蟮拇a組織方式,全部代碼如下。
類名為Arrow。它擁有x軸坐標(biāo)、y軸坐標(biāo)、底的顏色color、旋轉(zhuǎn)弧度rotation四個(gè)屬性。
實(shí)例方法是draw(),它需要一個(gè)context對象作為參數(shù),就是準(zhǔn)備工作里的context,它就相當(dāng)于是畫筆,這里其實(shí)是類似依賴注入的過程,將canvas的畫筆交給實(shí)例的draw()方法,實(shí)例用這個(gè)畫筆去畫出箭頭,繪畫過程見代碼注釋。特別注意以下幾點(diǎn):
beginPath()方法調(diào)用后moveTo()和lineTo移動(dòng)坐標(biāo)是相對與beginPath()時(shí)畫筆的坐標(biāo)的,可以理解成畫筆自帶一個(gè)坐標(biāo)系,它可以旋轉(zhuǎn)和在畫布上移動(dòng),繪制工作的坐標(biāo)都是屬于這個(gè)坐標(biāo)系的;
beginPath()是繪制設(shè)置狀態(tài)的起始點(diǎn),它之后代碼設(shè)置的繪制狀態(tài)的作用域結(jié)束于繪制方法stroke()、fill()或者closePath();
save()的作用是保存筆的狀態(tài),因?yàn)橐粋€(gè)畫布的筆只有一支,會(huì)在不同對象中傳遞,為了不污染后續(xù)的畫就應(yīng)該先保存,畫完再restore()還原;
本身是透明的,可以使用CSS給它個(gè)背景,例子中普遍使用白色背景。
/** * 箭頭類 * @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; // 會(huì)先保存畫筆狀態(tài) context.save(); // 移動(dòng)畫筆 context.translate(this.x, this.y); // 旋轉(zhuǎn)畫筆 context.rotate(this.rotation); // 設(shè)置線條寬度 context.lineWidth = 2; // 設(shè)置線條顏色 context.strokeStyle = "#ff0000"; // 設(shè)置填充顏色 context.fillStyle = this.color; // 開始路徑 context.beginPath(); // 將筆移動(dòng)到相對位置 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(); // 填充路徑包圍區(qū) context.fill(); // 繪制路徑 context.stroke(); // 載入保存的筆信息 context.restore(); } }
同理我們還可以畫點(diǎn)其他的,比如一個(gè)圓ball.js,稍微多些參數(shù),慢慢理解。
成品的效果可以先看這個(gè)(稍微劇透):一個(gè)會(huì)跟蹤鼠標(biāo)位置的箭頭
現(xiàn)在我們已經(jīng)掌握了畫畫的基本功,并且可以畫箭頭arrow.js和圓ball.js,然而這樣只是靜止畫,接下來我們需要一個(gè)循環(huán),不斷的執(zhí)行擦除和重畫的工作才能實(shí)現(xiàn)幀動(dòng)畫。
下面這段代碼的中繪圖函數(shù)drawFrame被立即執(zhí)行,并遞歸調(diào)用自身,你將會(huì)在大部分例子中看到。
循環(huán)原理上一篇已經(jīng)說明,不再重復(fù)。這里要說明的是clearRect(),這個(gè)函數(shù)接受一個(gè)矩形坐標(biāo),也就是(x軸坐標(biāo),y軸坐標(biāo),矩形寬度,矩形高度),用于清除矩形區(qū)域內(nèi)的畫。
例子里直接是清除了整個(gè)畫布,但這不是絕對的,刷不刷新,是局部刷新還是全部刷新,都需要靈活處理。
這里有個(gè)不刷新的例子:鼠標(biāo)畫圖工具
(function drawFrame() { // 類似setTimeout的操作 window.requestAnimationFrame(drawFrame, canvas); // 將畫布擦干凈 context.clearRect(0, 0, canvas.width, canvas.height); // ...繼續(xù)你的作畫 }());給它點(diǎn)動(dòng)力
現(xiàn)在畫面已經(jīng)是在不斷的重繪,但為什么還是靜止的呢?因?yàn)槊恳淮嗡⑿露紱]有改變要畫的內(nèi)容。
那我們就給它一個(gè)目標(biāo)吧,這樣它才能動(dòng)起來,比如就讓箭頭始終指向鼠標(biāo)。
下面是核心代碼,主要目的就是求出每幀arrow的旋轉(zhuǎn)角度,這里使用的工具類mouse會(huì)實(shí)時(shí)返回鼠標(biāo)的x,y軸坐標(biāo),封裝原理上一篇已經(jīng)講過,根據(jù)這鼠標(biāo)的坐標(biāo)和arrow的坐標(biāo),即可得到鼠標(biāo)的相對于arrow的距離dx和dy,如下圖:
而arrow的旋轉(zhuǎn)角度即可以通過dx和dy使用反正切函數(shù)得到,這里需要注意幾點(diǎn):
仔細(xì)看上面代碼中arrow的繪制過程,可知其原點(diǎn)是在中心位置的,所以剛好旋轉(zhuǎn)角度就是畫筆的旋轉(zhuǎn)角度;
dx和dy是鼠標(biāo)相對與arrow的坐標(biāo),所以圖中把坐標(biāo)系挪動(dòng)箭頭中心是沒毛病的;
用atan2,而不是atan,是因?yàn)閠an值本來就可能是重復(fù)的,比如-1/2和1/(-2)兩個(gè)都是-0.5,無法區(qū)分象限,而atan2就可以區(qū)分開。
完整實(shí)例:一個(gè)會(huì)跟蹤鼠標(biāo)位置的箭頭
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); }()); };三角函數(shù) 上下運(yùn)動(dòng)
終于順利過渡到三角函數(shù)的話題(笑)。三角函數(shù)不止有反正切一個(gè)應(yīng)用,下面再看一個(gè)例子。
下面是一個(gè)ball在上下運(yùn)動(dòng)的核心代碼,重點(diǎn)就是ball的y軸坐標(biāo)改變,就是這句:
ball.y = clientY + Math.sin(angle) * range;
利用Math.sin(angle)的取值范圍是-1到1,并且會(huì)隨著angle增大而反復(fù),使ball在一定范圍上下運(yùn)動(dòng)。
完整例子:一個(gè)上下運(yùn)動(dòng)的球(可調(diào)參數(shù)版)
window.onload = function () { const canvas = document.getElementById("canvas"); const context = canvas.getContext("2d"); const ball = new Ball(); let angle = 0; // 運(yùn)動(dòng)中心 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); }()); };向前運(yùn)動(dòng)
只是上下運(yùn)動(dòng)不過癮,那就讓圓前進(jìn)吧,其實(shí)就是每幀改變x軸的位置。
核心代碼如下,相比前面的上下運(yùn)動(dòng),多了x軸的速度,每幀移動(dòng)一點(diǎn)就形成了波浪前進(jìn)的效果。
完整實(shí)例:一個(gè)波浪運(yùn)動(dòng)的球
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); }()); };其他示例
其他的應(yīng)用就不一一講解,羅列出來一些:
不斷縮放的球
兩軸同時(shí)改變的圓
繪制波
一個(gè)做圓周運(yùn)動(dòng)的圓
一個(gè)做橢圓形運(yùn)動(dòng)的圓
計(jì)算兩個(gè)隨機(jī)塊的距離
中心點(diǎn)到鼠標(biāo)的距離
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/54524.html
摘要:前言上篇主要是理論的概述,本篇會(huì)多些實(shí)踐,來講講的基礎(chǔ)用法,并包含一些基礎(chǔ)三角函數(shù)的應(yīng)用,推薦沒有基礎(chǔ)的朋友閱讀,熟悉的朋友可以跳過。完整實(shí)例一個(gè)會(huì)跟蹤鼠標(biāo)位置的箭頭三角函數(shù)上下運(yùn)動(dòng)終于順利過渡到三角函數(shù)的話題笑。 前言 上篇主要是理論的概述,本篇會(huì)多些實(shí)踐,來講講canvas的基礎(chǔ)用法,并包含一些基礎(chǔ)三角函數(shù)的應(yīng)用,推薦沒有canvas基礎(chǔ)的朋友閱讀,熟悉的朋友可以跳過。 本人能力...
摘要:所建議的刷新率是秒幀,大部分瀏覽器是遵循這一標(biāo)準(zhǔn)的?;跁r(shí)間的動(dòng)畫其實(shí)無論是還是定時(shí)器,都不能保證以特定速率播放。將物體每幀移動(dòng)距離,轉(zhuǎn)變?yōu)槲矬w每秒移動(dòng)距離。 前言 本文雖說是基礎(chǔ)教程,但這是相對動(dòng)畫/游戲領(lǐng)域來說,在前端領(lǐng)域算是中級教程了,不適合前端小白或萌新。閱讀前請確保自己對前端三大件(JavaScript+CSS+HTML)的基礎(chǔ)已經(jīng)十分熟悉,而且有高中水平的數(shù)學(xué)和物理知識(shí)。d...
摘要:所建議的刷新率是秒幀,大部分瀏覽器是遵循這一標(biāo)準(zhǔn)的?;跁r(shí)間的動(dòng)畫其實(shí)無論是還是定時(shí)器,都不能保證以特定速率播放。將物體每幀移動(dòng)距離,轉(zhuǎn)變?yōu)槲矬w每秒移動(dòng)距離。 前言 本文雖說是基礎(chǔ)教程,但這是相對動(dòng)畫/游戲領(lǐng)域來說,在前端領(lǐng)域算是中級教程了,不適合前端小白或萌新。閱讀前請確保自己對前端三大件(JavaScript+CSS+HTML)的基礎(chǔ)已經(jīng)十分熟悉,而且有高中水平的數(shù)學(xué)和物理知識(shí)。d...
閱讀 3742·2021-11-24 10:23
閱讀 2788·2021-09-06 15:02
閱讀 1290·2021-08-23 09:43
閱讀 2364·2019-08-30 15:44
閱讀 3061·2019-08-30 13:18
閱讀 799·2019-08-23 16:56
閱讀 1758·2019-08-23 16:10
閱讀 555·2019-08-23 15:08