Javascript動畫(三):N個小球的躁動
阿新 • • 發佈:2018-12-24
目錄
上一小節我們實現了一個小球運動,太孤單了,現在我們來給它新增些小夥伴~
面向物件程式設計
我們先將編寫小球的類,基本就是將上一節小球屬性複製過來,只不過我們需在例項化時給它傳遞引數,用於控制小球屬性,畢竟每個小球長一樣也不好玩啦。
提示: 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個小球都有,且顏色及初始位置也不致
按鈕控制多次新增小球
細心的小夥伴,肯定發現上圖中有個“新增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;
}
}