--- title: 物件建構實作 slug: Learn/JavaScript/Objects/Object_building_practice tags: - Canvas - JavaScript translation_of: Learn/JavaScript/Objects/Object_building_practice ---
我們解說完必要的 JavaScript 物件理論以及語法細節,想先幫你把根紮好。接著就透過實作範例,讓你實際建立自己有趣又多彩的 JavaScript 物件。
必備條件: | 基礎的計算機素養、了解 HTML 與 CSS 的基本概念、熟悉 JavaScript (參閱〈First steps〉與〈Building blocks〉) 與 OOJS 基本概念 (參閱〈Introduction to objects〉)。 |
---|---|
要點: | 親手實作物件與物件導向 (OO) 技術。 |
本文將帶領你實作經典的「彈跳球」展示網頁,讓你了解物件在 JavaScript 中的用處。這些小球會在畫面上四處彈跳,而且互相碰撞時變換顏色。範例成品如下:
此範例將透過 Canvas API 在畫面上繪製球體,requestAnimationFrame API 則是繪製整個動畫;而且你不需先了解此兩個 API。但我們希望在看完本文之後,能引起大家深入探究此兩個 API 的興趣。整個過程會利用某些花俏的物件,並讓你看到幾項有趣技術,像是球體從牆上回彈,並檢查球體是否互相碰撞 (也就是碰撞偵測)。
先複製 index.html
、style.css
、main.js
檔案到你的本機磁碟中。這些檔案分別具備下列:
<h1> 的樣式風格與定位之用,並省去網頁邊緣的捲動棒或空白
(看起來更簡約)。<canvas>
元素,另有通用函式可供我們往後使用。指令碼第一部分就像:
var canvas = document.querySelector('canvas'); var ctx = canvas.getContext('2d'); var width = canvas.width = window.innerWidth; var height = canvas.height = window.innerHeight;
此指令碼將為 <canvas>
元素提供參照,接著於其上呼叫 getContext()
函式,藉以提供能開始繪圖的內文 (Context)。所產生的變數 (ctx
) 也就是物件,將直接呈現 canvas 的繪圖區域,讓我們繪製 2D 圖像。
接著設定 width
與 height
共 2 個變數,也就是 canvas 元素的寬度與高度 (透過 canvas.width
與 canvas.height
屬性呈現) 即等於瀏覽器可視區的寬度與高度 (也就是網頁顯示的區域 — 可經由 {{domxref("Window.innerWidth")}} 與 {{domxref("Window.innerHeight")}} 屬性得知)。
你會看到我們在這裡串連了多個指定式,以快速設定所有變數,而且運作無虞。
剛開始的指令碼後半部如下:
function random(min, max) { var num = Math.floor(Math.random()*(max-min)) + min; return num; }
此函式共有 2 組參數 (argument),並會回傳此範圍之內的任意值。
我們的程式會讓一堆彩球在畫面中彈來彈去。因為這些球體的行動方式均相同,所以透過物件呈現這些彩球也合情合理。先在程式碼底部加入下列建構子:
function Ball() { this.x = random(0,width); this.y = random(0,height); this.velX = random(-7,7); this.velY = random(-7,7); this.color = 'rgb(' + random(0,255) + ',' + random(0,255) + ',' + random(0,255) +')'; this.size = random(10,20); }
這裡我們要定義某些屬性,以利彩球能在程式中動作:
x
與 y
座標 — 每顆彩球都會先有位於畫面中的隨機水平\垂直座標。範圍從 0 (最左上角) 到瀏覽器可視區的寬\高度 (最右下角) 為止。velX
與 velY
) — 每顆彩球均具備隨機的速度值;在我們開始彩球動畫時,這些數值就會加上 x
/y
座標值,以利在各畫格(frame)中移動彩球。color
— 彩球的顏色是隨機產生。size
— 各彩球大小亦為隨機,從 10 到 20 像素不等。屬性講完了,那函式呢?程式中的彩球要實際運作才行。
先將下列 draw()
函式加到 Ball() 的
prototype 之中:
Ball.prototype.draw = function() { ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI); ctx.fill(); }
透過此函式,再呼叫我們之前定義在 2D canvas 內文(ctx)中的物件成員,就能讓球體自己在螢幕上畫出自己。此內文就像是白紙一樣,接著就用筆在紙上畫出點東西:
beginPath()
聲明我們要在紙上畫出來的形狀。fillStyle
定義該形狀所要呈現的顏色 — 設定為球體的 color
屬性。arc()
函式在紙上勾勒出弧形。相關參數為:
x
與 y
位置 — 我們指定球體的 x
與 y
屬性。size
屬性。2 * PI
度,這也等於半徑繞了 360 度 (你必須在半徑中指定,有點煩)。如此構成完整的圓。如果你只設定了 1 * PI,就會只有半球體
(即 180 度)。fill()
函式,基本上是用來聲稱「完成我們以 beginPath()
開始的繪圖路徑,再用 fillStyle 中指定的色彩將之填滿
」。你已經可以開始測試自己的物件了。
var testBall = new Ball();
testBall.x testBall.size testBall.color testBall.draw()
現在可以繪製彩球了。但在讓球彈跳之前,我們必須先更新幾個函式。將下列程式碼加到 JavaScript 檔案底端,把 update()
函式加到 Ball()
的 prototype
之中:
Ball.prototype.update = function() { if((this.x + this.size) >= width) { this.velX = -(this.velX); } if((this.x - this.size) <= 0) { this.velX = -(this.velX); } if((this.y + this.size) >= height) { this.velY = -(this.velY); } if((this.y - this.size) <= 0) { this.velY = -(this.velY); } this.x += this.velX; this.y += this.velY; }
函式的前 4 個部分負責檢查球體是否碰到 canvas 邊緣。如果球體抵達邊緣,我們就反轉相對加速度的方向,讓球反方向行進。以球體向上 (正向 velX
) 時為例,接著就會改變水平速度,球體也就反向運動。
在這 4 個情境中,我們:
x
座標是否大於 canvas 的寬度 (球體抵達右側邊緣)。x
座標是否小於 0 (球體抵達左側邊緣)。y
座標是否大於 canvas 的高度 (球體抵達底部邊緣)。y
座標是否小於 0 (球體抵達頂部邊緣)。在各情境中,因為 x
/y
座標為球體的中心,所以我們把球體的 size
納入計算,但我們不要球體在回彈之前在半路上就跳出畫面之外。
最後 2 行則是將 velX
與 velY
值分別加入 x\y
座標之中;每次只要呼叫此函式,球體就會依照應有的效果移動。
到這裡沒有問題的話,就開始弄動畫吧!
接著來玩玩吧。我們要加更多球到 canvas 中並開始動畫效果。
var balls = [];
所有可提供動畫效果的程式,一般都會採用動畫迴圈,可用以更新程式中的資訊,並接著在動畫的各個畫格上繪製產生的結果。這也是大部分遊戲或類似程式的基礎。
function loop() { ctx.fillStyle = 'rgba(0,0,0,0.25)'; ctx.fillRect(0,0,width,height); while(balls.length < 25) { var ball = new Ball(); balls.push(ball); } for(i = 0; i < balls.length; i++) { balls[i].draw(); balls[i].update(); } requestAnimationFrame(loop); }
我們的 loop()
函式可進行:
過 fillRect()
(共 4 個參數提供起始座標,以及繪製矩形的高度與寬度),跨 canvas 的寬度與高度繪製整個矩型的色彩。如此可在繪製下一個畫格之前,先覆蓋前一個已存在的畫格;否則會看到許多隻長長的蛇爬來爬去。填充顏色已設定為半透明狀態:rgba(0,0,0,0.25)
可讓先前的畫格微微發亮,製造出球體移動時的小尾巴效果。如果將 0.25 更改為 1,就會完全消除尾巴。你可自己測試不同的數值,找出自己喜歡的效果。Ball()
建立新的實作,接著將之 push()
到球體陣列的最後,且彩球數量必須少於 25 個。所以整個畫面最多顯示 25 個球。你可嘗試變更 balls.length < 25
中的數值,畫面中的彩球數量也會隨著變化。依你所用電腦\瀏覽器處理效能的不同,若繪製上千個彩球就會拖慢整個動畫的速度。balls
陣列中的所有彩球,並執行各個彩球的 draw()
與 update()
函式,以於畫面中逐一繪製,接著對下個畫格的位置與速度執行必要更新。requestAnimationFrame()
函式執行過此函式 — 當此函式持續執行並傳送相同的函式名稱時,就會每秒執行此函式達特定次數,以產生流暢的動畫。接著重複執行此作業,也就是函式每次執行時均會呼叫自身 1 次,進而循環執行。loop();
基本就是這樣了。試著儲存並重新整理檔案,讓你的彩球開始跳動吧!
現在弄點有趣的東西,就把碰撞偵測 (Collision detection) 加進程式裡,讓彩球知道自己碰到其他球了。
update()
函式中 (例如 Ball.prototype.update
區塊):
Ball.prototype.collisionDetect = function() { for(j = 0; j < balls.length; j++) { if( (!(this.x === balls[j].x && this.y === balls[j].y && this.velX === balls[j].velX && this.velY === balls[j].velY)) ) { var dx = this.x - balls[j].x; var dy = this.y - balls[j].y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < this.size + balls[j].size) { balls[j].color = this.color = 'rgb(' + random(0,255) + ',' + random(0,255) + ',' + random(0,255) +')'; } } } }
這函式有點複雜,所以現在不瞭解如何運作的也別擔心。解釋如下:
for
迴圈以循環檢視 balls[]
陣列中的所有彩球。if
陳述式檢查「現正透過迴圈循環檢查中」的彩球,是否即為我們目前檢查中的同一彩球。我們不需要檢查彩球是否碰撞到自己!為了達到此效果,我們檢查彩球目前的 x
/y
座標與速度,是否等同於迴圈檢查的彩球。接著透過「!
」否定檢查,所以在 if 陳述式中的程式碼,只有在彩球相異時才會執行。if
陳述式的程式碼。在本範例中,我們剛設定了 2 個球體的 color
屬性為新的隨機色彩。但當然可以更複雜點,像是讓彩球更逼真的互相反彈,但這實作起來就更複雜了。對這類的物理模擬,開發者就必須使用如 PhysicsJS、matter.js、Phaser 等的遊戲\物理函式庫。balls[i].update();
這一行下方新增下列程式碼即可:
balls[i].collisionDetect();
希望你喜歡撰寫出隨機彩球碰撞範例,其內並包含我們前面說過的多樣物件與 OO 技術!本文應該已提供你有用的物件實作與絕佳的實際文本。
物件實體就到這裡。接著就是你要磨練自己的物件技術了!
{{PreviousMenuNext("Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects/Adding_bouncing_balls_features", "Learn/JavaScript/Objects")}}