成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【30分鐘學(xué)完】canvas動(dòng)畫|游戲基礎(chǔ)(2):從零開始畫畫

anyway / 1921人閱讀

摘要:前言上篇主要是理論的概述,本篇會(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)文檔

準(zhǔ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)位置的箭頭

加入循環(huán)動(dòng)起來

現(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

相關(guān)文章

  • 30分鐘學(xué)完canvas動(dòng)畫|游戲基礎(chǔ)(2):從零開始畫畫

    摘要:前言上篇主要是理論的概述,本篇會(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ǔ)的朋友閱讀,熟悉的朋友可以跳過。 本人能力...

    Baoyuan 評論0 收藏0
  • 30分鐘學(xué)完canvas動(dòng)畫|游戲基礎(chǔ)(1):理論先行

    摘要:所建議的刷新率是秒幀,大部分瀏覽器是遵循這一標(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...

    wind5o 評論0 收藏0
  • 30分鐘學(xué)完canvas動(dòng)畫|游戲基礎(chǔ)(1):理論先行

    摘要:所建議的刷新率是秒幀,大部分瀏覽器是遵循這一標(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...

    wemall 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<