Object building practice - Learn web development

2018-05-15 17:26 更新
先決條件: 基本的計(jì)算機(jī)素養(yǎng),對HTML和CSS的基本了解,熟悉JavaScript基礎(chǔ)知識(請參見第一步構(gòu)建塊 / a>)和OOJS基礎(chǔ)知識(請參見對象簡介)。
目的: 要在現(xiàn)實(shí)世界環(huán)境中使用對象和面向?qū)ο蠹夹g(shù)來練習(xí)。

讓我們反彈一些球

在本文中,我們將編寫一個經(jīng)典的"彈跳球"演示,向您展示JavaScript中有用的對象。 我們的小球會在屏幕上反彈,并且當(dāng)它們彼此接觸時改變顏色。 完成的示例將看起來像這樣:

>

    本示例將使用 Canvas API 將球繪制到屏幕, 用于動畫顯示整個顯示的 requestAnimationFrame API - 您不需要 任何以前的這些API的知識,我們希望,當(dāng)你完成這篇文章,你會有興趣探索他們更多。 一路上,我們將使用一些漂亮的對象,并顯示一些很好的技術(shù),如彈跳球墻,并檢查他們是否相互擊中(否則稱為碰撞檢測)。

    入門

    首先,制作我們的 html"> index.html , bouncing-balls / style.css"> style.css /master/javascript/oojs/bouncing-balls/main.js\">main.js 文件。 它們分別包含以下內(nèi)容:

    1. A very simple HTML document featuring an <h1> element, a <canvas> element to draw our balls on, and elements to apply our CSS and JavaScript to our HTML.
    2. Some very simple styles, which mainly serve to style and position the <h1>, and get rid of any scrollbars or margin round the edge of the page (so that it looks nice and neat).
    3. Some JavaScript that serves to set up the <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;

    此腳本獲取對< canvas> 元素的引用,然后調(diào)用 / HTMLCanvasElement / getContext"> getContext() 方法,給我們一個上下文,我們可以開始繪制。 結(jié)果變量( ctx )是直接代表畫布的繪圖區(qū)域并允許我們在其上繪制2D形狀的對象。

    接下來,我們設(shè)置 width height 的變量,以及canvas元素的寬度和高度(由 canvas.width > canvas.height 屬性)等于瀏覽器視口的寬度和高度(網(wǎng)頁出現(xiàn)的區(qū)域 - 這可以從 zh-CN / docs / Web / API / Window / innerWidth"> Window.innerWidth / Web / API / Window / innerHeight"> Window.innerHeight 屬性)。

    你會看到這里,我們鏈接多個作業(yè)在一起,使變量所有設(shè)置更快 - 這是完全正常。

    初始腳本的最后一位如下所示:

    function random(min, max) {
      var num = Math.floor(Math.random() * (max - min + 1)) + min;
      return num;
    }

    此函數(shù)使用兩個數(shù)字作為參數(shù),并返回在兩者之間的范圍內(nèi)的隨機(jī)數(shù)。

    在我們的程序中建模一個球

    我們的程序?qū)⒂写罅康那蛟谄聊簧蠌椞?/span> 因?yàn)檫@些球?qū)⒁韵嗤姆绞奖憩F(xiàn),所以用對象來表示它們是有意義的。 讓我們從我們的代碼的底部添加下面的構(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);
    }

    這里我們定義一些屬性,每個我們的球需要在我們的程序中運(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).
    • horizontal and vertical velocity (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();
    }

    使用這個函數(shù),我們可以通過調(diào)用我們之前定義的2D畫布上下文的一系列成員( ctx )來告訴我們將自己繪制到屏幕上。 上下文就像紙,現(xiàn)在我們要命令我們的筆在它上面畫一些東西:

    • First, we use beginPath() to state that we want to draw a shape on the paper.
    • Next, we use fillStyle to define what color we want the shape to be — we set it to our ball's color property.
    • Next, we use the arc() method to trace an arc shape on the paper. Its parameters are:
      • The x and y position of the arc's center — we are specifying our ball's x and y properties.
      • The radius of our arc — we are specifying our ball's size property.
      • The last two parameters specify the start and end number of degrees round the circle that the arc is drawn between. Here we specify 0 degrees, and 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).
    • Last of all, we use the 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."

    你可以開始測試你的對象了。

    1. Save the code so far, and load the HTML file in a browser.
    2. Open the browser's JavaScript console, and then refresh the page so that the canvas size change to the smaller visible viewport left when the console opens.
    3. Type in the following to create a new ball instance:
      var testBall = new Ball();
    4. Try calling its members:
      testBall.x
      testBall.size
      testBall.color
      testBall.draw()
    5. When you enter the last line, you should see the ball draw itself somewhere on your canvas.

    更新球的數(shù)據(jù)

    我們可以把球放到位置,但是為了真正開始移動球,我們需要某種更新功能。 在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ù)的前四個部分檢查球是否已到達(dá)畫布的邊緣。 如果有,我們反轉(zhuǎn)相關(guān)速度的極性,使球在相反方向行進(jìn)。 因此,例如,如果球向上行進(jìn)(正 velX ),則改變水平速度,使得它開始向下行進(jìn)。

    在這四種情況下,我們是:

    • Checking to see whether the x coordinate is greater than the width of the canvas (the ball is going off the right hand edge).
    • Checking to see whether the x coordinate is smaller than 0 (the ball is going off the left hand edge).
    • Checking to see whether the y coordinate is greater than the height of the canvas (the ball is going off the bottom edge).
    • Checking to see whether the y coordinate is smaller than 0 (the ball is going off the top edge).

    在每種情況下,我們在計(jì)算中包括球的 size ,因?yàn)?code> x / y 坐標(biāo)位于球的中心, 但我們希望球的邊緣從周邊反彈 - 我們不想讓球在屏幕上半途開始反彈。

    最后兩行將 velX 值添加到 x 坐標(biāo)中,將 velY 值添加到 y 每次調(diào)用此方法時球都有效。

    這將為現(xiàn)在做; 讓我們開始一些動畫!

    動畫的球

    現(xiàn)在讓我們讓這個有趣。 我們現(xiàn)在要開始添加球到畫布,并動畫他們。

    1. First, we need somewhere to store all our balls. The following array will do this job — add it to the bottom of your code now:
      var balls = [];

      使動畫動畫的所有程序通常涉及動畫循環(huán),其用于更新程序中的信息,然后在動畫的每個幀上呈現(xiàn)結(jié)果視圖; 這是大多數(shù)游戲和其他此類程序的基礎(chǔ)。

    2. Add the following to the bottom of your code now:
      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í)行以下操作:

      • Sets the canvas fill color to semi-transparent black, then draws a rectangle of the color across the whole width and height of the canvas, using 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.
      • Creates a new instance of our 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!
      • loops through all the balls in the 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.
      • Runs the function again using the 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.
    3. Last but not least, add the following line to the bottom of your code — we need to call the function once to get the animation started.
      loop();

    這是它的基礎(chǔ) - 嘗試保存和刷新測試你的彈跳球出!

    添加沖突檢測

    現(xiàn)在有一點(diǎn)樂趣,讓我們添加一些碰撞檢測到我們的程序,所以我們的球?qū)⒅朗裁磿r候他們擊中另一個球。

    1. First of all, add the following method definition below where you defined the 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) +')';
            }
          }
        }
      }

      這個方法有點(diǎn)復(fù)雜,所以不要擔(dān)心,如果你不明白它的工作原理現(xiàn)在。 解釋如下:

      • For each ball, we need to check every other ball to see if it has collided with the current ball. To the end, we open up another for loop to loop through all the balls in the balls[] array.
      • Immediately inside our for loop, we use an 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.
      • We then use a common algorithm to check the collision of two circles. We are basically checking whether any of the two circle's areas overlap. This is explained further in 2D collision detection.
      • If a collision is detected, the code inside the inner 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.
    2. You also need to call this method in each frame of the animation. Add the following below the balls[i].update(); line:
      balls[i].collisionDetect();
    3. Save and refresh the demo again, and you'll see you balls change color when they collide!

    概要

    我們希望你有樂趣寫你自己的真實(shí)世界隨機(jī)彈跳球的例子,使用各種對象和面向?qū)ο蟮募夹g(shù)從整個模塊! 這應(yīng)該給你一些有用的實(shí)踐使用對象和好的現(xiàn)實(shí)世界上下文。

    這是對象文章 - 所有現(xiàn)在仍然是為了你測試你的技能在對象評估。

    也可以看看

    以上內(nèi)容是否對您有幫助:
    在線筆記
    App下載
    App下載

    掃描二維碼

    下載編程獅App

    公眾號
    微信公眾號

    編程獅公眾號