d3.js 制作简单的贪吃蛇
d3.js是一个不错的可视化框架,同时对于操作dom也是十分方便的。今天我们使用d3.js配合es6的类来制作一个童年小游戏–贪吃蛇。话不多说先上图片。
1. js snaker类
class Snaker {
constructor() {
this._size = 30;
this._len = 3;
this._width = 900;
this._height = 690;
this._rows = 23;
this._cols = 30;
this._colors = d3.scaleLinear().range(['#E75229','#FFBF35']);
this._svg = null;
this._currentArray = [[0,2],[0,1],[0,0]];
this._interval = null;
this._duration = 1000;
this._direction = 1;//上右下左0123
this._randomPosition = [0,6];
this.initSvg();
this.addKeyListener();
}
initSvg() {
this._svg = d3.select('.svg-container')
.append('svg')
.attr('width', this._width)
.attr('height', this._height)
this._svg.selectAll('line.rows')
.data(d3.range(this._rows))
.enter()
.append('line')
.attr('class', 'line rows')
.attr('x1', 0)
.attr('y1', d => d * this._size)
.attr('x2', this._width)
.attr('y2', d => d * this._size)
this._svg.selectAll('line.cols')
.data(d3.range(this._cols))
.enter()
.append('line')
.attr('class', 'line cols')
.attr('x1', d => d * this._size)
.attr('y1', 0)
.attr('x2', d => d * this._size)
.attr('y2', this._height)
}
addKeyListener() {
d3.select('body').on('keydown', () => {
switch (d3.event.keyCode) {
case 37:
this.rotate(3);
break;
case 38:
this.rotate(0);
break;
case 39:
this.rotate(1);
break;
case 40:
this.rotate(2);
break;
case 32:
console.log('空格');
break;
case 80:
console.log('暂停');
break;
default:
break;
}
})
}
rotate(num) {
if(num == this._direction) {
this.rotateMove();
} else if(num % 2 != this._direction % 2) {
this._direction = num;
this.rotateMove();
}
}
renderSnaker() {
this._svg.selectAll('rect.active').remove();
this._svg.selectAll('rect.active')
.data(this._currentArray)
.enter()
.append('rect')
.attr('class', 'active')
.attr('x', d => d[1] * this._size)
.attr('y', d => d[0] * this._size)
.attr('width', this._size)
.attr('height', this._size)
.attr('fill', (d,i) => this._colors(i / this._len))
.attr('stroke', (d,i) => this._colors(i / this._len))
}
canMove() {
//下一步没有触碰边缘
let noTouchBorder = true;
//下一步没有触碰自身
let noTouchSelf = true;
//新数组
let newArray = [];
//判断方向
switch(this._direction) {
case 0:
if(this._currentArray[0][0] == 0) {
noTouchBorder = false;
} else {
newArray = this._currentArray.map((c,i,arr) => {
if(i == 0) {
return [c[0] - 1, c[1]]
} else {
return arr[i - 1]
}
})
}
break;
case 1:
if(this._currentArray[0][1] == this._cols - 1) {
noTouchBorder = false;
} else {
newArray = this._currentArray.map((c,i,arr) => {
if(i == 0) {
return [c[0], c[1] + 1]
} else {
return arr[i - 1]
}
})
}
break;
case 2:
if(this._currentArray[0][0] == this._rows - 1) {
noTouchBorder = false;
} else {
newArray = this._currentArray.map((c,i,arr) => {
if(i == 0) {
return [c[0] + 1, c[1]]
} else {
return arr[i - 1]
}
})
}
break;
case 3:
if(this._currentArray[0][1] == 0) {
noTouchBorder = false;
} else {
newArray = this._currentArray.map((c,i,arr) => {
if(i == 0) {
return [c[0], c[1] - 1]
} else {
return arr[i - 1]
}
})
}
break;
}
//判断新数组第一个元素是否出现在后面其他元素中
for(var i=1; i<newArray.length; i++) {
if(newArray[0][0] == newArray[i][0] && newArray[0][1] == newArray[i][1]) {
noTouchSelf = false;
}
}
return noTouchBorder && noTouchSelf;
}
setScoreAndSpeed() {
d3.select('#score').html(this._len);
d3.select('#speed').html((this._duration * (1 - this._len / 1000) / 1000).toString().substr(0,8) + 's')
}
moveArray() {
if(this.canMove()) {
if(this._direction == 0) {
if(this._currentArray[0][0] - 1 == this._randomPosition[0] && this._currentArray[0][1] == this._randomPosition[1]) {
this._currentArray.unshift(this._randomPosition);
this._len ++;
this.setScoreAndSpeed();
this.removeRandomPosition();
this.randomPosition();
} else {
this._currentArray.unshift([this._currentArray[0][0] - 1,this._currentArray[0][1]])
this._currentArray.pop();
}
} else if(this._direction == 1) {
if(this._currentArray[0][0] == this._randomPosition[0] && this._currentArray[0][1] + 1 == this._randomPosition[1]) {
this._currentArray.unshift(this._randomPosition);
this._len ++;
this.setScoreAndSpeed();
this.removeRandomPosition();
this.randomPosition();
} else {
this._currentArray.unshift([this._currentArray[0][0],this._currentArray[0][1] + 1])
this._currentArray.pop();
}
} else if(this._direction == 2) {
if(this._currentArray[0][0] + 1 == this._randomPosition[0] && this._currentArray[0][1] == this._randomPosition[1]) {
this._currentArray.unshift(this._randomPosition);
this._len ++;
this.setScoreAndSpeed();
this.removeRandomPosition();
this.randomPosition();
} else {
this._currentArray.unshift([this._currentArray[0][0] + 1,this._currentArray[0][1]])
this._currentArray.pop();
}
} else if(this._direction == 3) {
if(this._currentArray[0][0] == this._randomPosition[0] && this._currentArray[0][1] - 1 == this._randomPosition[1]) {
this._currentArray.unshift(this._randomPosition);
this._len ++;
this.setScoreAndSpeed();
this.removeRandomPosition();
this.randomPosition();
} else {
this._currentArray.unshift([this._currentArray[0][0],this._currentArray[0][1] - 1])
this._currentArray.pop();
}
}
} else {
console.log('game over');
alert('game over')
}
}
removeRandomPosition() {
d3.selectAll('rect.random').remove();
}
randomPosition() {
let random = Math.floor(Math.random() * (this._cols * this._rows - this._len));
let temp = [];
for(var i=0; i<this._rows; i++) {
for(var j=0; j<this._cols; j++) {
temp.push([i,j])
}
}
let emptyArray = temp.filter(a => !this._currentArray.some(b => b[0] == a[0] && b[1] == a[1]));
this._randomPosition = emptyArray[random];
this._svg.append('rect')
.attr('class', 'random')
.attr('x', this._randomPosition[1] * this._size)
.attr('y', this._randomPosition[0] * this._size)
.attr('width', this._size)
.attr('height', this._size)
}
interval() {
this._interval = setInterval(() => {
this.moveArray();
this.renderSnaker();
}, this._duration * (1 - this._len / 1000))
}
//转弯附带移动一次
rotateMove() {
this.moveArray();
this.renderSnaker();
}
initData() {
this._currentArray = [[0,2],[0,1],[0,0]];
}
start() {
this.initData();
this.renderSnaker();
this.interval();
this.randomPosition();
this.setScoreAndSpeed();
}
}
2. css 代码
* {
padding:;
margin:;
}
.container {
width: 100vw;
height: 100vh;
}
.svg-container {
margin: 50px;
width: 900px;
height: 690px;
border: 3px double #666;
display: inline-block;
overflow: hidden;
}
aside {
width: 200px;
height: 300px;
display: inline-block;
vertical-align: top;
margin-top: 50px;
}
.line {
shape-rendering: crispEdges;
stroke: #bbbbbb;
}
.active {
stroke-width:;
fill-opacity: 0.5;
}
.random {
fill: #ff00ff;
fill-opacity: 0.5;
stroke: #ff00ff;
stroke-width:;
}
3. html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>$Title$</title>
<link rel="stylesheet" type="text/css" href="css/base.css"/>
<script type="text/javascript" src="js/d3.v4.js"></script>
<script type="text/javascript" src="js/base.js"></script>
</head>
<body>
<div class="container">
<div class="svg-container"></div>
<aside>
<table>
<tr>
<td>当前分数:</td>
<td id="score"></td>
</tr>
<tr>
<td>当前速度:</td>
<td id="speed"></td>
</tr>
</table>
<button onclick="start()">开始游戏</button>
</aside>
</div>
<script>
var snaker = new Snaker();
function start() {
snaker.start();
} </script>
</body>
</html>
有想预览或者下载demo的朋友请移步至个人博客
原文地址 http://www.bettersmile.cn
d3.js 制作简单的贪吃蛇的更多相关文章
- d3.js 制作简单的俄罗斯方块
d3.js是一个不错的可视化框架,同时对于操作dom也是十分方便的.今天我们使用d3.js配合es6的类来制作一个童年小游戏--俄罗斯方块.话不多说先上图片. 1. js tetris类 由于方法拆分 ...
- js编写简单的贪吃蛇游戏
css代码 *{ margin:; padding:; } td{ width: 4px; height: 4px; background: #ccc; border: 2px solid #ccc; ...
- 使用JS制作小游戏贪吃蛇
先看效果图: 过程如下: 1.首先创建一张画布地图<div class="map"> </div>: 2.创建食物的自调用函数 (function (){ ...
- C#简单实现贪吃蛇程序(LinQ + Entity)
做梦想起来的C#简单实现贪吃蛇程序(LinQ + Entity) 最近一直在忙着单位核心开发组件的版本更新,前天加了一个通宵,昨天晚上却睡不着,脑子里面突然不知怎的一直在想贪吃蛇的实现方法.以往也有类 ...
- TOJ 3973 Maze Again && TOJ 3128 简单版贪吃蛇
TOJ3973传送门:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=3973 时间限制(普通 ...
- D3.js 制作中国地图 .net 公共基础类
D3.js 制作中国地图 from: http://d3.decembercafe.org/pages/map/index.html GeoJSON is a format for encoding ...
- Java一个简单的贪吃蛇
Java一个简单的贪吃蛇 虽然GUI已经要淘汰了,但是手动写写界面还是有助于理解语法的,像构造函数 ,函数调用,内部类,继承,接口.有助于半初学者强化理解. 直接上代码 游戏主体类: package ...
- GUI简单实战——贪吃蛇
将前面学到的GUI基础知识完成实战,完成一个简单的贪吃蛇项目 项目功能 用键盘上下左右实现贪吃蛇的自动移动 贪吃蛇吃到食物后,长度加一,分数加一 贪吃蛇吃到自己的身体,则游戏结束 按空格键实现游戏的暂 ...
- d3.js制作连线动画图和编辑器
此文章为原创文章,原文地址:https://www.cnblogs.com/eagle1098/p/11431679.html 连线动画图 编辑器 效果如上图所示.本项目使用主要d3.jsv4制作,分 ...
随机推荐
- MySQL Schema与数据类型优化
Schema与数据类型优化 选择优化的数据类型 1.更小的通常更好 更小的数据类型通常更快,因为它们占用更少的磁盘,内存和CPU缓存 2.简单就好 简单数据类型的操作通常需要更少的CPU周期.例如:整 ...
- JavaFX 选择文件 导入Excel文件并解析
FXML 控制器 : @FXML public void selectExcel(MouseEvent event) { FileChooser fileChooser = new FileChoos ...
- Activiti6系列(1)- 核心数据库表及字段注释说明
前言 本文是根据<疯狂工作流讲义-Activiti6.0>一书中提取过来的,有兴趣的可以去当当网买这本书,讲的很不错,最后还有实战案例. 虽然是提取过来的,但完全靠手打梳理,觉得有用的小伙 ...
- 转载 | embed用法(网站中视频、音频的添加)
网站中添加视频: <embed src="http://player.video.qiyi.com/390cf6c74450e4c70b7bd2d883169914/0/0/w_19r ...
- IDEA+maven搭建scala开发环境(spark)(半转载)
以下内容部分来自于https://zhuanlan.zhihu.com/p/23141509,我尝试了一遍,然后添加了一些图片.. 其实我觉得在IDEA中使用scala插件然后创建project的时候 ...
- intellIJ IDEA学习笔记
如果你初次用idea,毫无目的的度娘如何使用IDEA 浪费的将会是大量的时间.为以表诚意, 上一套IDEA教学视频,以表我诚意.(下载地址:https://pan.baidu.com/s/1g ...
- CSV Data Set Config 拓展开发
1.目的 在日常的性能测试项目中,经常会遇到参数化的问题,我们所熟知的LR工具对参数化支持非常友好,然而JMeter相对逊色一些.大家都知道在使用JMeter工具参数化时常用CSV Data Set ...
- Redis 学习笔记(篇九):主从复制
Redis 中,可以通过执行 savleof 命令或者设置 slaveof 选项,让一个服务器去复制另一个服务器,我们称被复制的服务器为主服务器,而对主服务器进行复制的服务器则被称为从服务器. Red ...
- Homebrew 安装 Docker Desktop for Mac
无意中发现Homebrew现在已经支持Docker Desktop for Mac了,因此特意把原来通过 https://docs.docker.com/docker-for-mac/install/ ...
- C++函数中,两个自动释放内存的动态内存申请类
最近做一个事情,实现一个流程交互,其中主交互流程函数中,涉及较多的内存申请, 而健康的函数,都是在函数退出前将手动申请不再需要的内存释放掉, 使用很多方法,都避免不了较多的出错分支时,一堆的if fr ...