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;//上右下左0123this._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: 0;margin: 0; } .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: 2;fill-opacity: 0.5; } .random {fill: #ff00ff;fill-opacity: 0.5;stroke: #ff00ff;stroke-width: 2; }
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