根据MDN网站学习记录笔记
JavaScript对象
本项目主要是测试对JavaScript对象和面向对象结构的理解。
一、项目简介
首先在GitHub上面下载起始代码:index.html、 style.css 和 main.js
- 为弹球demo添加一个由玩家控制的“恶魔圈”,如果恶魔圈抓到弹球会把它会吃掉。
- 首先创建一个通用 Shape() 对象,然后由它派生出弹球和恶魔圈。
- 最后,为 demo 添加一个计分器来记录剩下的球数。
二、项目流程
2.1 创建新对象Shape()并添加一个新的构造器Ball()
改变你现有的构造器 Ball()
使其成为构造器 Shape()
并添加一个新的构造器 Ball()
,其中draw(), update(), collisionDetect()
方法定义应保持不变。
// 定义Shape() 构造器
function Shape(x, y, velX, velY, exists) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.exists = exists;
}
// 定义 Ball 构造器
function Ball(x, y, velX, velY, exists, color, size) {
Shape.call(this, x, y, velX, velY, exists);
this.color = color;
this.size = size;
}
2.2 定义恶魔圈EvilCircle()
EvilCircle()
构造器应该从Shape()
继承 x, y, 和 exists ,velX 和 velY 要恒为 20。
// 定义EvilCircle() 构造器
function EvilCircle(x, y, exists){
Shape.call(this, x, y, exists);
this.velX = 20;
this.velY = 20;
this.color = 'white';
this.size = 10;
}
2.3 定义 EvilCircle() 的方法
- draw()
使恶魔圈变成空心,并且边缘加粗。
// 定义恶魔圈绘制函数
EvilCircle.prototype.draw = function() {
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.stroke();
};
- checkBounds()
类似于弹球的更新函数,要修改 x/y 的值,使恶魔圈稍微地弹回屏幕,通过加减size实现。
// 定义恶魔圈边缘检测函数
EvilCircle.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;
}
};
- setControls()
这里的 window.onkeydown
用一个箭头函数代替了英文页面中的匿名函数
// 给window对象设置onkeydown键盘监听器
EvilCircle.prototype.setControls = function(){
window.onkeydown = e => {
switch(e.key) {
case 'a':
this.x -= this.velX;
break;
case 'd':
this.x += this.velX;
break;
case 'w':
this.y -= this.velY;
break;
case 's':
this.y += this.velY;
break;
}
};
}
- collisionDetect()
类似于弹球的碰撞检测,不同的是:这里的彭咋黄检测首先要检查循环到的小球是否存在,不存在则无需比较了。除此之外,需要将发生碰撞的小球的exists属性设置为不存在。
// 定义碰撞检测函数
EvilCircle.prototype.collisionDetect = function() {
for(let j = 0; j < balls.length; j++) {
if(balls[j].exists === true) {
const dx = this.x - balls[j].x;
const dy = this.y - balls[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
// 因为distance是两个圆心之间的距离!所以得小于两个圆的半径之和!
if (distance < this.size + balls[j].size) {
balls[j].exists = false;
}
}
}
};
2.4 把恶魔圈带到程序中
在loop()
函数之前,创建一个新的恶魔圈的对象实例 (指定必需的参数),然后调用它的 setControls()
方法。
// 创建恶魔圈的实例并且使其坐标为随机数
evil = new EvilCircle(random(0, width), random(0, width), true);
// 启动监听
evil.setControls();
修改loop()
函数
draw(), update(),
和collisionDetect()
函数的调用进行修改, 使这些函数只会在小球存在时被调用。- 调用恶魔圈实例的方法
draw(), checkBounds(), 和collisionDetect()
// 定义一个循环来不停地播放
function loop() {
ctx.fillStyle = 'rgba(0,0,0,0.25)';
ctx.fillRect(0, 0, width, height);
for(let i = 0; i < balls.length; i++) {
if(balls[i].exists){
balls[i].draw();
balls[i].update();
balls[i].collisionDetect();
}
evil.draw();
evil.checkBounds();
evil.collisionDetect();
}
requestAnimationFrame(loop);
}
2.5 计算得分
- 在HTML中添加一个
元素并在css添加相应样式
- 在js中创建变量存储段落的引用
// 存储段落的引用
const para = document.querySelector('p');
// 设置全局变量count
let count = 0;
- 恶魔圈碰撞检测中减少count,在屏幕上显示小球数量
if (distance < this.size + balls[j].size) {
balls[j].exists = false;
count--;
para.textContent = "剩余彩球数:" + count;
}
- 创建小球数组时增加count,在屏幕上显示小球数量
balls.push(ball);
count++;
para.textContent = '剩余彩球数:' + count;
三、效果展示
四、章节小结
4.1 对象继承中的call()函数
JavaScript中对象的继承不是复制来的,而是通过原型链继承。
function.call(thisArg, arg1, arg2, ...)
thisArg
可选的。在 function 函数运行时使用的 this 值。如果为null
或undefined
则会自动替换为指向全局对象。
call()
函数使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它。
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
4.2 用Object.create实现类式继承
Object.create(proto,[propertiesObject])
其中proto
是新创建对象的原型对象propertiesObject
可选。
返回值是一个带着指定原型对象和属性的新对象
// Shape - 父类(superclass)
function Shape() {
this.x = 0;
this.y = 0;
}
// 父类的方法
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - 子类(subclass)
function Rectangle() {
Shape.call(this); // call super constructor.
}
// 子类续承父类
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?',
rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
4.3 Canvas 2D中fillStyle和strokestyle
fillStyle
: 设置填充图形的颜色,渐变和模式。strokeStyle
: 设置用于笔触(描边)的颜色,渐变和模式。
两者语法类似:
ctx.fillStyle = color;
ctx.fillStyle = gradient;
ctx.fillStyle = pattern;