1. 程式人生 > >Javascript動畫(三):N個小球的躁動

Javascript動畫(三):N個小球的躁動

目錄


上一小節我們實現了一個小球運動,太孤單了,現在我們來給它新增些小夥伴~

面向物件程式設計

我們先將編寫小球的類,基本就是將上一節小球屬性複製過來,只不過我們需在例項化時給它傳遞引數,用於控制小球屬性,畢竟每個小球長一樣也不好玩啦。
提示: Object.assign 是es6的介面,文章結尾給了個polyfill版本

function
Ball(options) { var opts = Object.assign({ x: 50, // x座標 y: 50, // y座標 r: 30, // 小球半徑 color: 'blue', // 小球填充顏色 vx: 10, // 小球x軸速度 px/幀 vy: 10 // 小球y軸速度 px/幀 }, options); this.x = opts.x; this.y = opts.y; this.r = opts.r; this.color = opts.color; this
.vx = opts.vx; this.vy = opts.vy; }

再將上一節的update與draw函式作為此類的方法

/**
 * 更新小球位置,基於幀速率
 * @param dt 此幀離上幀間隔時間
 */
Ball.prototype.update = function (dt) {
  var that = this;
  that.x += that.vx;
  that.y += that.vy;
  if (that.x - that.r <= 0 ) {                  // 小球碰到了左邊
    that.x = that.r;
    that.
vx *= -1; } else if (that.x + that.r >= cvs.width) { // 小球碰到了右邊 that.x = cvs.width - that.r; that.vx *= -1; } if (that.y - that.r <= 0 ) { // 小球碰到了上邊 that.y = that.r; that.vy *= -1; } else if (that.y + that.r >= cvs.height) { // 小球碰到了下邊 that.y = cvs.height - that.r; that.vy *= -1; } }; /** * 將小球畫到畫布上顯示出來 * @param ctx 畫面上下文物件 */ Ball.prototype.draw = function (ctx) { var that = this; // 不能在此處作清除背景的操作啦,畢竟我是要畫多個小球,需放到animate函式中 // ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.fillStyle = that.color; ctx.beginPath(); ctx.arc(that.x, that.y, that.r, 0, Math.PI * 2, false); ctx.closePath(); ctx.fill(); };

修改上一小節的邏輯,看看更改為類的方式,是否執行出錯

var cvs = document.getElementById('canvas'),    // 獲取畫布元素
    ctx = cvs.getContext('2d');                 // 獲取畫面上下文物件,用於畫圖
cvs.width = 800;        // 設定畫布寬度
cvs.height = 600;       // 設定畫布高度

// 小球相關屬性
var ball = new Ball();      // 使用預設屬性例項化一個小球

var lasttime = undefined;
function animate(time) {
  if (lasttime === undefined) {
    lasttime = time;
  } else {
    var dt = time - lasttime;
    lasttime = time;
    // 清屏操作放到此處,每一幀只需清屏一次
    ctx.clearRect(0, 0, cvs.width, cvs.height);
    ball.update(dt);                 // 更新小球位置
    ball.draw(ctx);                     // 更新後,將小球畫出來
  }
  requestAnimationFrame(animate);
}

requestAnimationFrame(animate); // 呼叫requestAnimationFrame,使瀏覽器在最佳時機執行回撥函式

新增多個小球

上一步執行程式碼沒出錯,小球跑的很歡樂~
此步就給它加幾夥伴了。

var cvs = document.getElementById('canvas'),    // 獲取畫布元素
    ctx = cvs.getContext('2d');                 // 獲取畫面上下文物件,用於畫圖
cvs.width = 800;        // 設定畫布寬度
cvs.height = 600;       // 設定畫布高度

// 定義存放小球陣列
var balls = [];

/**
 * 新增小球的函式
 * @param n  需要新增多少個小球,若不傳或傳非數字則表示新增1個小球
 */
function addBalls(n) {
  n = n || parseInt(n);
  if (isNaN(n)) {
    n = 1;
  }
  for (var i = 0; i < n; ++i) {
    balls.push(new Ball({
      x: Math.random() * (cvs.width - 200) + 100,       // 隨機位置
      y: Math.random() * (cvs.height - 200) + 100,
      r: Math.random() * 10 + 20,                      // 隨機半徑
      // 隨機填充色
      color: 'rgb('+(Math.random() * 200 + 55 )+','+(Math.random() * 200 + 55 )+','+(Math.random() * 200 + 55 )+')'
      // 速度使用預設的
    }));
  }
}
// 小球相關屬性
// var ball = new Ball();      // 使用預設屬性例項化一個小球

// 例項化多個小球,此處10個
addBalls(10);

var lasttime = undefined;
function animate(time) {
  if (lasttime === undefined) {
    lasttime = time;
  } else {
    var dt = time - lasttime;
    lasttime = time;
    // 清屏操作放到此處,每一幀只需清屏一次
    ctx.clearRect(0, 0, cvs.width, cvs.height);
    // ball.update(dt);                 // 更新小球位置
    // ball.draw(ctx);                     // 更新後,將小球畫出來
    // 遍歷所有小球,更新所有小球位置,並畫出來
    balls.forEach(function (ball) {
      ball.update(dt);
      ball.draw(ctx);
    })
  }
  requestAnimationFrame(animate);
}

requestAnimationFrame(animate); // 呼叫requestAnimationFrame,使瀏覽器在最佳時機執行回撥函式

執行程式碼可以看到,10個小球都有,且顏色及初始位置也不致
10個小球的躁動

按鈕控制多次新增小球

細心的小夥伴,肯定發現上圖中有個“新增100個小球”的按鈕,我們現在來實現此操作。

css更改為如下程式碼:

body{
	text-align: center;
}
#container{
   margin-top: 30px;
   display: inline-block;
   border-radius: 10px;
   box-shadow: 0 0 10px 0 #999;
   padding: 20px;
}
canvas{
   background-color: #fff;
   border: 1px solid #000;
}
button{
   margin-bottom: 20px;
}

html更改為如下程式碼:

<div id="container">
    <button type="button" id="addBallBtn">新增100個小球</button>
    <div>
        <canvas id="canvas"></canvas>
    </div>
</div>

javascript程式碼只需在結尾新增如下程式碼:

var cvs = document.getElementById('canvas'),    // 獲取畫布元素
// ....
// 省略其它未更改的程式碼
// ....
requestAnimationFrame(animate); // 呼叫requestAnimationFrame,...

var addBallBtn = document.getElementById('addBallBtn');
addBallBtn.addEventListener('click', function () {
  addBalls(100);
}, false);

執行程式碼,嘗試點選按鈕新增多個小球,看看執行效果。

幾千上萬小球的躁動

連續不斷的點選新增小球的按鈕,當小球個數達到幾千上萬時,你會明顯感覺小球跑的沒剛開始快了,理論上我們設定的小球的速度沒做更改,它的速度就應該不會變啊。這是為什麼呢?且看下回分解 0.0

Object.assign 的polyfill

如下是我寫的個Object.assign的polyfill版本,需要的可以看看

Object.assign = Object.assign || function () {
 var args = Array.prototype.slice.call(arguments, 0);
 if (args.length === 0) {
   throw new Error("必需傳入至少一個引數");
 }
 if (Object.prototype.toString.call(args[0]) !== '[object Object]') {
   throw new Error("第一個引數必須為Object型別");
 }
 if (args.length === 1) {
   return args[0];
 } else {
   var result = args[0];
   for (var i = 1, len = args.length; i < len; ++i) {
     if (Object.prototype.toString.call(args[i]) === '[object Object]') {
       for (var key in args[i]) {
         if (args[i].hasOwnProperty(key)) {
           result[key] = args[i][key]
         }
       }
     }
   }
   return result;
 }
}