一、 框架
1、标题用h1。
2、计数器用p。
3、小球和环都用canvas绘制。
二、功能
1、小球相互碰撞会变颜色。
2、通过键盘改变环的位置。
3、小球碰到环会消失,计数器数量减少。
4、小球会一直移动,碰到边缘会弹回。
三、知识点
1、用面向对象的思想,创建Shape对象,并由他派生出Ball球和Eaters环对象。
// 定义Shape构造器
function Shape(x, y, velX, velY, exists) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
// 用来标记球是否存在于程序中 (没有被恶魔圈吃掉)
this.exists = exists;
}
// 定义Ball构造器,继承自Shape
function Ball(x, y, velX, velY, exists, color, size) {
Shape.call(this, x, y, velX, velY, exists);
this.color = color;
this.size = size;
}
// 给构造器 Ball() 的prototype 和 constructor 属性设置适当的值
Ball.prototype = Object.create(Shape.prototype);
Ball.prototype.constructor = Ball;
// 定义Ball绘制函数:重点研究一下这个写法
Ball.prototype.draw = function () {
ctx.beginPath();
ctx.fillStyle = this.color; // why?
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
};
// 实例化对象Ball,并传入参数
let ball = new Ball(
// 为避免绘制错误,球至少离画布边缘球本甚一倍宽度的距离
random(0 + size, width - size),
random(0 + size, height - size),
random(-7, 7),
random(-7, 7),
true,
randomColor(),
size
);
2、监听键盘事件,并判断方向
// 定义大胃王控制设置方法
Eaters.prototype.setControls = function () {
window.onkeydown = e => {
switch (e.key) {
case 'a':
case 'A':
case 'ArrowLeft':
this.x -= this.velX;
break;
case 'd':
case 'D':
case 'ArrowRight':
this.x += this.velX;
break;
case 'w':
case 'W':
case 'ArrowUp':
this.y -= this.velY;
break;
case 's':
case 'S':
case 'ArrowDown':
this.y += this.velY;
break;
}
};
};
3、随机数函数、随机颜色函数的编写
// 生成随机数
function random(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
// 生成随机颜色值
function randomColor() {
return 'rgb(' + random(0, 255) + ',' + random(0, 255) + ',' + random(0, 255) + ')';
}
4、边界判断
// 定义彩球边界测试函数,若球超出边界,更新 velX/velY
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;
};
// 定义大胃王的边缘检测方法,若超出边界,我们通过增加或减去环的size,来改变x,y的值
Eaters.prototype.checkBounds = function () {
if ((this.x + this.size) >= width) {
this.x -= this.size;
}
if ((this.x - this.size) <= 0) {
this.x += this.size;
}
if ((this.y + this.size) >= height) {
this.y -= this.size;
}
if ((this.y - this.size) <= 0) {
this.y += this.size;
}
};
5、canvas绘制
// 设置画布:通过获取要绘制的 <canvas> 元素,然后调用它的HTMLCanvasElement.getContext()方法
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;
// 背景设为黑色,
// strokeStyle和stroke(0是设置图形轮廓(边框)的颜色,fillStyle 和 fill() 是设置图形填充(边框以内)的颜色
ctx.fillStyle = 'rgba(0,0,0,0.25)';
ctx.fillRect(0, 0, width, height);
// canvas.toDataURL('image/jpeg');
// 定义彩球绘制函数:重点研究一下这个写法
Ball.prototype.draw = function () {
ctx.beginPath();
ctx.fillStyle = this.color; // why?
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
};
6、动画--计时器
requestAnimationFrame的用法与settimeout很相似,只是不需要设置时间间隔而已。requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行。
应用
现在分别使用setInterval、setTimeout和requestAnimationFrame这三个方法制作一个简单的进制度效果
1 setInterval
<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
clearInterval(timer);
myDiv.style.width = '0';
timer = setInterval(function(){
if(parseInt(myDiv.style.width) < 500){
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
myDiv.innerHTML = parseInt(myDiv.style.width)/5 + '%';
}else{
clearInterval(timer);
}
},16);
}
</script>
2 setTimeout
<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
clearTimeout(timer);
myDiv.style.width = '0';
timer = setTimeout(function fn(){
if(parseInt(myDiv.style.width) < 500){
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
myDiv.innerHTML = parseInt(myDiv.style.width)/5 + '%';
timer = setTimeout(fn,16);
}else{
clearTimeout(timer);
}
},16);
}
</script>
3 requestAnimationFrame
<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
myDiv.style.width = '0';
cancelAnimationFrame(timer);
timer = requestAnimationFrame(function fn(){
if(parseInt(myDiv.style.width) < 500){
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
myDiv.innerHTML = parseInt(myDiv.style.width)/5 + '%';
timer = requestAnimationFrame(fn);
}else{
cancelAnimationFrame(timer);
}
});
}
</script>
7、碰撞
// 定义碰撞检测函数
Ball.prototype.collisionDetect = function () {
for (var j = 0; j < balls.length; j++) {
//检验循环到的小球是否是当前 collisionDetect() 小球,不是的话就让当前小球和其他小球依次计算直线距离,距离小于两个球的半径和就碰在一起了
if (this != balls[j]) {
const dx = this.x - balls[j].x;
const dy = this.y - balls[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.size + balls[j].size && balls[j].exists) {
// 碰一起后小球颜色发生改变
balls[j].color = this.color = randomColor();
}
}
}
};
// 定义大胃王与小球碰撞的函数,要检查小球是不是存在,如果小球不存在,说明它已经被恶魔圈吃掉了,那么就不需要再检测它是否与恶魔圈碰撞了。
Eaters.prototype.collisionDetect = function () {
for (let j = 0; j < balls.length; j++) {
if (balls[j].exists) {
const dx = this.x - balls[j].x;
const dy = this.y - balls[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.size + balls[j].size) {
balls[j].exists = false;
count--;
para.textContent = '剩余彩球数:' + count;
}
}
}
};
遇到的问题:
1. 如何改变canvas的背景颜色?
因为canvas整个画布相当于一张图片,所以就是如何改变图片的颜色。
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, width, height);
2. vsCode的 live server插件失效了
百度的解决方法是:若失效则换用Preview on Web Server插件为同样效果。
几分钟后不知道为什么又可以用了,推测是网络问题。
3. notepad 如何对比两个文件
插件》compare安装》重启后点插件》compare》compare即可