「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」。
写在前面
- 最近在整理 JavaScript 面向对象的知识点,不得不说面向对象还是很经典的编程范式
- 通过对现实世界事物的抽象,将事物的行为与属性进行封装聚合,需要使用时,直接实例化出一个对象,通过对对象的操作来实现我们想要的程序
- 光说不练假把式,直接使用面向对象的思想实现一个贪吃蛇小游戏
- 为了体现面向对象的优雅,本文代码均使用原生 JavaScript 编写
- 下面是效果图:
- 蛇头部元素默认为绿色,蛇身元素默认为白色,食物颜色随机,出现位置随机
抽象
- 我们要实现的是:贪吃蛇游戏
- 提到
贪吃蛇
,那肯定是要有食物
和蛇
这两个比较显而易见的类 - 蛇 需要有运动的场地,那么我们可以再抽象一个
地图
类 - 我们实现的是一个游戏,那游戏的主流程需要抽象为一个类吗?
- 按照面向对象提倡的封装思想,我认为是需要的
- 将游戏主流程封装为一个类,可以让我们的整个程序实现更加清晰,代码也会随之变得优雅
- 如果未来,我们想要换一中贪吃蛇的玩法,我们只需要替换这个
游戏类
的实现逻辑即可,这也体现了遵循面向对象范式的代码易于维护的特点
- 总结一下,我们想要实现贪吃蛇游戏,需要实现 4 个类
-
地图类
-
食物类
-
蛇类
-
游戏类
-
实现地图类
- 很显然,地图类需要负责地图上的所有元素的渲染逻辑
class Map {
/**
* el 表示地图的 dom 元素
* rect 单元格的宽高
*/
constructor({ el, rect }) {
this.el = el;
this.rect = rect || 10;
// 存放 蛇 与 食物 的位置和颜色数据
this.data = [];
this.rows = 0;
this.columns = 0;
this.adjustMap();
}
// 清空地图上的元素数据
clear() {
this.data.length = 0;
}
// 判断传入的数据 是否 已经存在于当前地图上了
check({ x, y }) {
return !!this.data.find((i) => {
i.x === x && i.y === y;
});
}
setData(newData) {
this.data = this.data.concat(newData);
}
adjustMap() {
const { el, rect } = this;
// 单元格的长和宽
this.rows = Math.ceil(Map.getStyle(el, 'height') / rect);
this.columns = Math.ceil(Map.getStyle(el, 'width') / rect);
// 根据划分的格子,反向修正地图的长宽
Map.setStyle(el, 'height', this.rows * rect);
Map.setStyle(el, 'width', this.columns * rect);
}
static getStyle(el, attr) {
return parseInt(getComputedStyle(el)[attr]);
}
static setStyle(el, attr, num) {
el.style[attr] = num + 'px';
}
// 渲染地图上的元素
render() {
this.el.innerHTML = this.data
.map((i) => {
return `
<span
style="position: absolute;
left: ${i.x * this.rect}px;
top: ${i.y * this.rect}px;
width: ${this.rect}px;
height: ${this.rect}px;
background: ${i.color}"
>
</span>
`;
})
.join('');
}
}
初始化
- 在上面的地图内中,我们在初始化时,接收两个参数
- el,表示的是地图的 dom 元素
- rect,表示地图中每个单元格的宽高,为了方便计算,规定每个单元格都是正方形
- rect 默认为
10px
- rect 默认为
- 最后初始化一个数组 data,用于存放蛇身体的坐标数据,以及食物的坐标数据
反向修正地图宽高
- 由于我们示例化地图时,可能存在 rect 不能整数倍填满整个地图的情况,为了避免这样的尴尬情况发生,我们需要实现一个反向修正地图宽高的功能,如代码所示的三个函数
adjustMap
、getStyle
、setStyle
- 根据传入的 el 得到地图真实宽高,然后除以 rect 计算出实际的单元格数量
- 再根据单元格数量和传入的单元格大小,算出实际地图应有的宽高
渲染地图上的元素
- 实现方法很简单,通过 data 中数据的坐标信息,生成一个个 span 标签
- 然后将标签字符串塞到地图 dom 元素的 innerHTML 中
小结
- 到这里,我们的地图类,实现的差不多了
- 大家可以看到上面代码中,还有三个方法:
clear
、setData
、check
,没有提到 - 我们按需实现,在后面的文章中,会根据实际需求再来介绍这些方法的用处
- 为了不影响阅读体验,我会将这个 demo 的实现过程分为 4 篇文章来写,下一篇实现食物类
最后
- 今天的分享就到这里,欢迎大家在评论区留言讨论
- 如果觉得文章写的还不错的话,希望大家不要吝惜点赞,大家的鼓励是我分享的最大动力 🥰