Javascript动画(三):N个小球的躁动

本文通过面向对象编程方式,实现多个小球在画布上的动态效果,包括小球的随机生成、运动更新及碰撞处理,同时介绍了如何通过按钮控制小球数量的增减。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


上一小节我们实现了一个小球运动,太孤单了,现在我们来给它添加些小伙伴~

面向对象编程

我们先将编写小球的类,基本就是将上一节小球属性复制过来,只不过我们需在实例化时给它传递参数,用于控制小球属性,毕竟每个小球长一样也不好玩啦。
提示: 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;
 }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值