codecamp

Object building practice - Learn web development

先决条件: 基本的计算机素养,对HTML和CSS的基本了解,熟悉JavaScript基础知识(请参见第一步构建块 / a>)和OOJS基础知识(请参见对象简介)。
目的: 要在现实世界环境中使用对象和面向对象技术来练习。

让我们反弹一些球

在本文中,我们将编写一个经典的"弹跳球"演示,向您展示JavaScript中有用的对象。 我们的小球会在屏幕上反弹,并且当它们彼此接触时改变颜色。 完成的示例将看起来像这样:

>

    本示例将使用 Canvas API 将球绘制到屏幕, 用于动画显示整个显示的 requestAnimationFrame API - 您不需要 任何以前的这些API的知识,我们希望,当你完成这篇文章,你会有兴趣探索他们更多。 一路上,我们将使用一些漂亮的对象,并显示一些很好的技术,如弹跳球墙,并检查他们是否相互击中(否则称为碰撞检测)。

    入门

    首先,制作我们的 html"> index.html bouncing-balls / style.css"> style.css /master/javascript/oojs/bouncing-balls/main.js\">main.js 文件。 它们分别包含以下内容:

    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> 元素的引用,然后调用 / HTMLCanvasElement / getContext"> getContext() 方法,给我们一个上下文,我们可以开始绘制。 结果变量( ctx )是直接代表画布的绘图区域并允许我们在其上绘制2D形状的对象。

    接下来,我们设置 width height 的变量,以及canvas元素的宽度和高度(由 canvas.width > canvas.height 属性)等于浏览器视口的宽度和高度(网页出现的区域 - 这可以从 zh-CN / docs / Web / API / Window / innerWidth"> Window.innerWidth / Web / API / Window / innerHeight"> Window.innerHeight 属性)。

    你会看到这里,我们链接多个作业在一起,使变量所有设置更快 - 这是完全正常。

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

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

    此函数使用两个数字作为参数,并返回在两者之间的范围内的随机数。

    在我们的程序中建模一个球

    我们的程序将有大量的球在屏幕上弹跳。 因为这些球将以相同的方式表现,所以用对象来表示它们是有意义的。 让我们从我们的代码的底部添加下面的构造函数。

    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 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();
    }

    使用这个函数,我们可以通过调用我们之前定义的2D画布上下文的一系列成员( ctx )来告诉我们将自己绘制到屏幕上。 上下文就像纸,现在我们要命令我们的笔在它上面画一些东西:

    • 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.

    更新球的数据

    我们可以把球放到位置,但是为了真正开始移动球,我们需要某种更新功能。 在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;
    }

    函数的前四个部分检查球是否已到达画布的边缘。 如果有,我们反转相关速度的极性,使球在相反方向行进。 因此,例如,如果球向上行进(正 velX ),则改变水平速度,使得它开始向下行进。

    在这四种情况下,我们是:

    • 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).

    在每种情况下,我们在计算中包括球的 size ,因为 x / y 坐标位于球的中心, 但我们希望球的边缘从周边反弹 - 我们不想让球在屏幕上半途开始反弹。

    最后两行将 velX 值添加到 x 坐标中,将 velY 值添加到 y 每次调用此方法时球都有效。

    这将为现在做; 让我们开始一些动画!

    动画的球

    现在让我们让这个有趣。 我们现在要开始添加球到画布,并动画他们。

    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 = [];

      使动画动画的所有程序通常涉及动画循环,其用于更新程序中的信息,然后在动画的每个帧上呈现结果视图; 这是大多数游戏和其他此类程序的基础。

    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()函数执行以下操作:

      • 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();

    这是它的基础 - 尝试保存和刷新测试你的弹跳球出!

    添加冲突检测

    现在有一点乐趣,让我们添加一些碰撞检测到我们的程序,所以我们的球将知道什么时候他们击中另一个球。

    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) +')';
            }
          }
        }
      }

      这个方法有点复杂,所以不要担心,如果你不明白它的工作原理现在。 解释如下:

      • 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!

    概要

    我们希望你有乐趣写你自己的真实世界随机弹跳球的例子,使用各种对象和面向对象的技术从整个模块! 这应该给你一些有用的实践使用对象和好的现实世界上下文。

    这是对象文章 - 所有现在仍然是为了你测试你的技能在对象评估。

    也可以看看

    Working with JSON data
    Adding features to our bouncing balls demo
    温馨提示
    下载编程狮App,免费阅读超1000+编程语言教程
    取消
    确定
    目录
    CSS

    关闭

    MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }