先決條件: | 基本的計(jì)算機(jī)素養(yǎng),對(duì)HTML和CSS的基本了解,熟悉JavaScript基礎(chǔ)知識(shí)(請(qǐng)參見(jiàn)第一步和構(gòu)建塊 / a>)和OOJS基礎(chǔ)知識(shí)(請(qǐng)參見(jiàn)對(duì)象簡(jiǎn)介)。 |
---|---|
目的: | 要在現(xiàn)實(shí)世界環(huán)境中使用對(duì)象和面向?qū)ο蠹夹g(shù)來(lái)練習(xí)。 |
在本文中,我們將編寫(xiě)一個(gè)經(jīng)典的"彈跳球"演示,向您展示JavaScript中有用的對(duì)象。 我們的小球會(huì)在屏幕上反彈,并且當(dāng)它們彼此接觸時(shí)改變顏色。 完成的示例將看起來(lái)像這樣:
>
本示例將使用 Canvas API 將球繪制到屏幕, 用于動(dòng)畫(huà)顯示整個(gè)顯示的 requestAnimationFrame API - 您不需要 任何以前的這些API的知識(shí),我們希望,當(dāng)你完成這篇文章,你會(huì)有興趣探索他們更多。 一路上,我們將使用一些漂亮的對(duì)象,并顯示一些很好的技術(shù),如彈跳球墻,并檢查他們是否相互擊中(否則稱(chēng)為碰撞檢測(cè))。
首先,制作我們的 html"> index.html
, bouncing-balls / style.css"> style.css
和 /master/javascript/oojs/bouncing-balls/main.js\">main.js
文件。 它們分別包含以下內(nèi)容:
<h1>
element, a <canvas>
element to draw our balls on, and elements to apply our CSS and JavaScript to our HTML.<h1>
, and get rid of any scrollbars or margin round the edge of the page (so that it looks nice and neat).<canvas>
element and provide a general function that we're going to use.腳本的第一部分如下所示:
var canvas = document.querySelector('canvas'); var ctx = canvas.getContext('2d'); var width = canvas.width = window.innerWidth; var height = canvas.height = window.innerHeight;
此腳本獲取對(duì)< canvas>
元素的引用,然后調(diào)用 / HTMLCanvasElement / getContext"> getContext()
方法,給我們一個(gè)上下文,我們可以開(kāi)始繪制。 結(jié)果變量( ctx
)是直接代表畫(huà)布的繪圖區(qū)域并允許我們?cè)谄渖侠L制2D形狀的對(duì)象。
接下來(lái),我們?cè)O(shè)置 width
和 height
的變量,以及canvas元素的寬度和高度(由 canvas.width
和 > canvas.height
屬性)等于瀏覽器視口的寬度和高度(網(wǎng)頁(yè)出現(xiàn)的區(qū)域 - 這可以從 zh-CN / docs / Web / API / Window / innerWidth"> Window.innerWidth
和 / Web / API / Window / innerHeight"> Window.innerHeight
屬性)。
你會(huì)看到這里,我們鏈接多個(gè)作業(yè)在一起,使變量所有設(shè)置更快 - 這是完全正常。
初始腳本的最后一位如下所示:
function random(min, max) { var num = Math.floor(Math.random() * (max - min + 1)) + min; return num; }
此函數(shù)使用兩個(gè)數(shù)字作為參數(shù),并返回在兩者之間的范圍內(nèi)的隨機(jī)數(shù)。
我們的程序?qū)⒂写罅康那蛟谄聊簧蠌椞?/span> 因?yàn)檫@些球?qū)⒁韵嗤姆绞奖憩F(xiàn),所以用對(duì)象來(lái)表示它們是有意義的。 讓我們從我們的代碼的底部添加下面的構(gòu)造函數(shù)。
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); }
這里我們定義一些屬性,每個(gè)我們的球需要在我們的程序中運(yùn)行:
x
and y
coordinates — each ball is initially given a random horizontal and vertical coordinate where it will start on the screen. This can range between 0 (top left hand corner) to the width and height of the browser viewport (bottom right hand corner).velX
and velY
) — each ball is given random values to represent its velocity; in real terms these values will be regularly added to the x
/y
coordinate values when we start to animate the balls, to move them by this much on each frame.color
— each ball gets a random color to start with.size
— each ball gets a random size, a radius of between 10 and 20 pixels.這將屬性排序,但是方法怎么樣? 我們想要在我們的程序中做一些事情。
首先向 Ball()
的原型
中添加以下 draw()
Ball.prototype.draw = function() { ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI); ctx.fill(); }
使用這個(gè)函數(shù),我們可以通過(guò)調(diào)用我們之前定義的2D畫(huà)布上下文的一系列成員( ctx
)來(lái)告訴我們將自己繪制到屏幕上。 上下文就像紙,現(xiàn)在我們要命令我們的筆在它上面畫(huà)一些東西:
beginPath()
to state that we want to draw a shape on the paper.fillStyle
to define what color we want the shape to be — we set it to our ball's color
property.arc()
method to trace an arc shape on the paper. Its parameters are: x
and y
position of the arc's center — we are specifying our ball's x
and y
properties.size
property.2 * PI
, which is the equivalent of 360 degrees in radians (annoyingly, you have to specify this in radians). That gives us a complete circle. If you had specified only 1 * PI
, you'd get a semi-circle (180 degrees).fill()
method, which basically states "finish drawing the path we started with beginPath()
, and fill the area it takes up with the color we specified earlier in fillStyle
."你可以開(kāi)始測(cè)試你的對(duì)象了。
var testBall = new Ball();
testBall.x testBall.size testBall.color testBall.draw()
我們可以把球放到位置,但是為了真正開(kāi)始移動(dòng)球,我們需要某種更新功能。 在JavaScript文件底部添加以下代碼,以向 Ball()
的原型
中添加 update()
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; }
函數(shù)的前四個(gè)部分檢查球是否已到達(dá)畫(huà)布的邊緣。 如果有,我們反轉(zhuǎn)相關(guān)速度的極性,使球在相反方向行進(jìn)。 因此,例如,如果球向上行進(jìn)(正 velX ),則改變水平速度,使得它開(kāi)始向下行進(jìn)。
在這四種情況下,我們是:
x
coordinate is greater than the width of the canvas (the ball is going off the right hand edge).x
coordinate is smaller than 0 (the ball is going off the left hand edge).y
coordinate is greater than the height of the canvas (the ball is going off the bottom edge).y
coordinate is smaller than 0 (the ball is going off the top edge).在每種情況下,我們?cè)谟?jì)算中包括球的 size
,因?yàn)?code> x / y
坐標(biāo)位于球的中心, 但我們希望球的邊緣從周邊反彈 - 我們不想讓球在屏幕上半途開(kāi)始反彈。
最后兩行將 velX
值添加到 x
坐標(biāo)中,將 velY
值添加到 y
每次調(diào)用此方法時(shí)球都有效。
這將為現(xiàn)在做; 讓我們開(kāi)始一些動(dòng)畫(huà)!
現(xiàn)在讓我們讓這個(gè)有趣。 我們現(xiàn)在要開(kāi)始添加球到畫(huà)布,并動(dòng)畫(huà)他們。
var balls = [];
使動(dòng)畫(huà)動(dòng)畫(huà)的所有程序通常涉及動(dòng)畫(huà)循環(huán),其用于更新程序中的信息,然后在動(dòng)畫(huà)的每個(gè)幀上呈現(xiàn)結(jié)果視圖; 這是大多數(shù)游戲和其他此類(lèi)程序的基礎(chǔ)。
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()
函數(shù)執(zhí)行以下操作:
fillRect()
(the four parameters provide a start coordinate, and a width and height for the rectangle drawn). This serves to cover up the previous frame's drawing before the next one is drawn. If you don't do this, you'll just see long snakes worming their way around the canvas instead of balls moving! The color of the fill is set to semi-transparent, rgba(0,0,0,0.25)
, to allow the previous few frames to shine through slightly, producing the little trails behind the balls as they move. If you changed 0.25 to 1, you won't see them at all any more. Try varying this number to see the effect it has.Ball()
, then push()
es it onto the end of our balls array, but only while the number of balls in the array is less than 25. So when we have 25 balls on screen, no more balls appear. You can try varying the number in balls.length < 25
to get more of less balls on screen. Depending on how much processing power your computer/browser has, specifying several thousand balls might slow down the animation rather a lot!balls
array, and runs each ball's draw()
and update()
function to draw each one on the screen, then do the necessary updates to position and velocity in time for the next frame.requestAnimationFrame()
method — when this method is constantly run and passed the same function name,?it will run that function a set number of times per second to create a smooth animation. This is generally done recursively — which means that the function is calling itself every time it runs, so it will run over and over again.loop();
這是它的基礎(chǔ) - 嘗試保存和刷新測(cè)試你的彈跳球出!
現(xiàn)在有一點(diǎn)樂(lè)趣,讓我們添加一些碰撞檢測(cè)到我們的程序,所以我們的球?qū)⒅朗裁磿r(shí)候他們擊中另一個(gè)球。
update()
method (i.e. the Ball.prototype.update
block). Ball.prototype.collisionDetect = function() { for (j = 0; j < balls.length; j++) { if (!(this === balls[j])) { 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) +')'; } } } }
這個(gè)方法有點(diǎn)復(fù)雜,所以不要擔(dān)心,如果你不明白它的工作原理現(xiàn)在。 解釋如下:
for
loop to loop through all the balls in the balls[]
array.if
statement to check whether the current ball being looped through is the same ball as the one we are currently checking. We don't want to check whether a ball has collided with itself! To do this, we check whether the current ball (i.e., the ball whose collisionDetect method is being invoked)?is?the same as the loop ball (i.e., the ball that is being referred to by the current iteration of the for loop in the collisionDetect method). We then use !
to negate the check, so that the code inside the the if statement only runs if they are not the same.if
statement is run. In this case we are just setting the color
property of both the circles to a new random color. We could have done something far more complex, like get the balls to bounce off each other realistically, but that would have been far more complex to implement. For such physics simulations, developers tend to use a games or physics library such as PhysicsJS, matter.js, Phaser, etc.balls[i].update();
line: balls[i].collisionDetect();
注意:如果您無(wú)法使此示例生效,請(qǐng)嘗試將您的JavaScript代碼與我們的 blob / master / javascript / oojs / bouncing-balls / main-finished.js">完成版本(也見(jiàn) -area / javascript / oojs / bouncing-balls / index-finished.html">正在運(yùn)行)。
我們希望你有樂(lè)趣寫(xiě)你自己的真實(shí)世界隨機(jī)彈跳球的例子,使用各種對(duì)象和面向?qū)ο蟮募夹g(shù)從整個(gè)模塊! 這應(yīng)該給你一些有用的實(shí)踐使用對(duì)象和好的現(xiàn)實(shí)世界上下文。
這是對(duì)象文章 - 所有現(xiàn)在仍然是為了你測(cè)試你的技能在對(duì)象評(píng)估。
更多建議: