d3.js 制作简单的俄罗斯方块

d3.js是一个不错的可视化框架,同时对于操作dom也是十分方便的。今天我们使用d3.js配合es6的类来制作一个童年小游戏--俄罗斯方块。话不多说先上图片。

1. js tetris类

由于方法拆分的比较细所以加上了一些备注(这不是我的风格!)

const graphMap = [{name: '倒梯形',position: [[0,4],[1,3],[1,4],[1,5]],rotate: [[[0,0],[-2,0],[-1,-1],[0,-2]],[[1,0],[1,2],[0,1],[-1,0]],[[-1,0],[1,0],[0,1],[-1,2]],[[0,0],[0,-2],[1,-1],[2,0]]],color: '#D7DF01'},{name: '一字型',position: [[0,3],[0,4],[0,5],[0,6]],rotate: [[[-1,1],[0,0],[1,-1],[2,-2]],[[1,2],[0,1],[-1,0],[-2,-1]],[[2,-2],[1,-1],[0,0],[-1,1]],[[-2,-1],[-1,0],[0,1],[1,2]]],color: '#0000FF'},{name: '正方形',position: [[0,4],[0,5],[1,4],[1,5]],rotate: [[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]]],color: '#FF0000'},{name: 'Z字型',position: [[0,3],[0,4],[1,4],[1,5]],rotate: [[[0,1],[1,0],[0,-1],[1,-2]],[[1,1],[0,0],[-1,1],[-2,0]],[[1,-2],[0,-1],[1,0],[0,1]],[[-2,0],[-1,1],[0,0],[1,1]]],color: '#800080'},{name: '反Z字型',position: [[1,3],[1,4],[0,4],[0,5]],rotate: [[[-1,1],[0,0],[1,1],[2,0]],[[0,1],[-1,0],[0,-1],[-1,-2]],[[2,0],[1,1],[0,0],[-1,1]],[[-1,-2],[0,-1],[-1,0],[0,1]]],color: '#FFA500'},{name: 'L字型',position: [[1,3],[0,3],[0,4],[0,5]],rotate: [[[-1,1],[0,2],[1,1],[2,0]],[[0,1],[1,0],[0,-1],[-1,-2]],[[1,-1],[0,-2],[-1,-1],[-2,0]],[[0,-1],[-1,0],[0,1],[1,2]]],color: '#90EE90'},{name: '反L字型',position: [[0,3],[0,4],[0,5],[1,5]],rotate: [[[-1,2],[0,1],[1,0],[0,-1]],[[2,0],[1,-1],[0,-2],[-1,-1]],[[1,-2],[0,-1],[-1,0],[0,1]],[[-2,0],[-1,1],[0,2],[1,1]]],color: '#AEEBFF'}
]
class Tetris {constructor() {this._grid = [];this._rows = 18;this._cols = 10;this._div = 33;this._nextDiv = 15;this._duration = 1000;this._width = this._div * this._cols;this._height = this._div * this._rows;this._svg = null;this._nextSvg = null;this._timeout = null;this._time = null;this._showGrid = false;this._haveArray = [];this._curtArray = [];this._colors = '';this._rotateIndex = 0;this._rotateArray = [];this._fixedColor = '#666';this._nextNumber = 0;this._graphMap = graphMap;this._level = 1;this._levelLimit = [0,20,50,90,140,200,270,350,440,540,650,770,900,1040,1190,1350,1520];this._score = 0;this._timeNumber = 0;this.initSvg();this.initNextSvg();this.addKeyListener();}initSvg() {this._svg = d3.select('.svg-container').append('svg').attr('width', this._width).attr('height', this._height).attr('transform', 'translate(0, 4)')}initNextSvg() {this._nextSvg = d3.select('.next').append('svg').attr('width', 100).attr('height', 60)}toggleGrid() {if(this._showGrid) {this._showGrid = false;d3.select('g.grid').remove();} else {this._showGrid = true;this._grid = this._svg.append('g').attr('class', 'grid')this._grid.selectAll('line.row').data(d3.range(this._rows)).enter().append('line').attr('class', 'row').attr('x1', 0).attr('y1', d => d * this._div).attr('x2', this._width).attr('y2', d => d * this._div)this._grid.selectAll('line.col').data(d3.range(this._cols)).enter().append('line').attr('class', 'col').attr('x1', d => d * this._div).attr('y1', 0).attr('x2', d => d * this._div).attr('y2', this._height)}}addKeyListener() {d3.select('body').on('keydown', () => {switch (d3.event.keyCode) {case 37:this.goLeft();break;case 38:this.rotate();break;case 39:this.goRight();break;case 40:this.goDown();break;case 32:console.log('空格');break;case 80:console.log('暂停');break;default:break;}})}//设置运动图形 如果仍有掉落空间则继续掉落 反之调用setHaveArray
    initGraph() {this.renderGraph();this._timeout = setTimeout(() => {if(this.canDown()) {this.downArray();this.initGraph();} else {this.setHaveArray();if(!this.gameOver()) {this.randomData();this.nextGraphNumber();this.initGraph();} else {clearTimeout(this._time);d3.select('#modal').style('top', '0px')}}}, this._duration * (1 - ((this._level - 1) / this._levelLimit.length) / 2))}//渲染图形
    renderGraph() {this._svg.selectAll('rect.active').remove();this._svg.selectAll('rect.active').data(this._curtArray).enter().append('rect').attr('class', 'active').attr('x', d => this._div * d[1] + 1).attr('y', d => this._div * d[0] + 1).attr('width', this._div - 3).attr('height', this._div - 3).attr('stroke', this._color).attr('stroke-width', 2).attr('fill', this._color).attr('fill-opacity', 0.5)}//设置掉落后的数组,并清除运动的图形 重置状态
    setHaveArray() {this._curtArray.forEach(d => this._haveArray.push(d));this._svg.selectAll('rect.active').attr('class', 'fixed').attr('fill', this._fixedColor).attr('fill-opacity', 0.5).attr('stroke', this._fixedColor);this._rotateIndex = 0;this.clearLines();}//检测有满列 然后消除
    clearLines() {let clearLinesArr = [];let allRowsObj = {};let temp = [];let arr = this._haveArray.map(d => d[0]);arr.forEach(d => {if(allRowsObj.hasOwnProperty(d)) {allRowsObj[d] ++} else {allRowsObj[d] = 1;}})for(var i in allRowsObj) {if(allRowsObj[i] == this._cols) {clearLinesArr.push(i)}}if(clearLinesArr.length != 0) {this.setScoreAndLevel(clearLinesArr.length);this._haveArray = this._haveArray.filter(a => !clearLinesArr.some(b => b == a[0]));this._haveArray = this._haveArray.map(d => [this.downSome(d[0],clearLinesArr), d[1]])this._svg.selectAll('rect.fixed').remove();this._svg.selectAll('rect.fixed').data(this._haveArray).enter().append('rect').attr('class', 'fixed').attr('x', d => this._div * d[1] + 1).attr('y', d => this._div * d[0] + 1).attr('width', this._div - 3).attr('height', this._div - 3).attr('stroke', this._fixedColor).attr('stroke-width', 2).attr('fill', this._fixedColor).attr('fill-opacity', 0.5)}}//消除时 判断下落层数
    downSome(c, arr) {let num = 0;arr.forEach(d => {if(c < d) {num ++;}})return num + c;}//设置等级和分数
    setScoreAndLevel(num) {switch(num) {case 1:this._score = this._score + 1;break;case 2:this._score = this._score + 3;break;case 3:this._score = this._score + 6;break;case 4:this._score = this._score + 10;default:break;}for(var i=0; i<this._levelLimit.length; i++) {if(this._score <= this._levelLimit[i]) {this._level = i + 1;break;}}d3.select('#score').html(this._score);d3.select('#level').html(this._level);}//左移动
    goLeft() {if(this.canLeft()) {this.leftArray();this.renderGraph();}}//右移动
    goRight() {if(this.canRight()) {this.rightArray();this.renderGraph();}}//旋转
    rotate() {if(this.canRotate()) {this.rotateArray();this.renderGraph();}}//下移动
    goDown() {if(this.canDown()) {this.downArray();this.renderGraph();}}//下落更新数组
    downArray() {this._curtArray = this._curtArray.map(d => {return [d[0] + 1, d[1]]})}//左移更新数组
    leftArray() {this._curtArray = this._curtArray.map(d => {return [d[0], d[1] - 1]})}//右移更新数组
    rightArray() {this._curtArray = this._curtArray.map(d => {return [d[0], d[1] + 1]})}//旋转更新数组
    rotateArray() {let arr = this._rotateArray[this._rotateIndex];this._curtArray = this._curtArray.map((d,i) => {return [d[0] + arr[i][0], d[1] + arr[i][1]]})this._rotateIndex = (this._rotateIndex + 1) % 4;}//判断是否可以下落
    canDown() {let max = 0;let status = true;let nextArr = this._curtArray.map(d => {if(d[0] + 1 > max) {max = d[0];}return [d[0] + 1, d[1]]});nextArr.forEach(d => {this._haveArray.forEach(item => {if(item[0] == d[0] && item[1] == d[1]) {status = false;}})})if(!status || max > 16) {return false;} else {return true;}}//判断是否可以左移
    canLeft() {let min = this._cols;let status = true;let nextArr = this._curtArray.map(d => {if(d[1] - 1 < min) {min = d[1];}return [d[0], d[1] - 1]})nextArr.forEach(d => {this._haveArray.forEach(item => {if(item[0] == d[0] && item[1] == d[1]) {status = false;}})})if(!status || min <= 0) {return false;} else {return true;}}//判断是否可以右移
    canRight() {let max = 0;let status = true;let nextArr = this._curtArray.map(d => {if(d[1] + 1 > max) {max = d[1];}return [d[0], d[1] + 1]})nextArr.forEach(d => {this._haveArray.forEach(item => {if(item[0] == d[0] && item[1] == d[1]) {status = false;}})})if(!status || max > this._cols - 2) {return false;} else {return true;}}//判断可以变形
    canRotate() {let max = 0;let min = this._cols;let status = true;let arr = this._rotateArray[this._rotateIndex];let nextArr = this._curtArray.map((d,i) => {if(d[1] + 1 > max) {max = d[1];}if(d[1] - 1 < min) {min = d[1];}return [d[0] + arr[i][0], d[1] + arr[i][1]]})if(!status || max > this._cols - 1 || min < 0) {return false;} else {return true;}}//判断游戏结束
    gameOver() {let status = false;this._haveArray.forEach(d => {if((d[0] == 0 && d[1] == 3) || (d[0] == 0 && d[1] == 4) || (d[0] == 0 && d[1] == 5) || (d[0] == 0 && d[1] == 6)) {status = true;}})return status;}//随机生成图形块
    randomData() {this._curtArray = this._graphMap[this._nextNumber].position;this._color = this._graphMap[this._nextNumber].color;this._rotateArray = this._graphMap[this._nextNumber].rotate;}//预设下一个图形展示
    nextGraphNumber() {let rand = [0,0,1,1,2,2,3,4,5,6];this._nextNumber = rand[Math.floor(Math.random() * 10000) % 10];this._nextSvg.selectAll('rect.ne').remove();this._nextSvg.selectAll('rect.ne').data(this._graphMap[this._nextNumber].position).enter().append('rect').attr('class', 'ne').attr('x', d => this._nextDiv * (d[1] - 1) + 1).attr('y', d => this._nextDiv * (d[0] + 1) + 1).attr('width', this._nextDiv - 3).attr('height', this._nextDiv - 3).attr('stroke', this._graphMap[this._nextNumber].color).attr('stroke-width', 2).attr('fill', this._graphMap[this._nextNumber].color).attr('fill-opacity', 0.5)}//初始化数据
    initData() {this._haveArray = [];this._level = 1;this._score = 0;this._timeNumber = 0;this._svg.selectAll('rect').remove();d3.select('#score').html(0);d3.select('#level').html(1);d3.select('#time').html(0);}//开始时间
    initTime() {this._time = setInterval(() => {this._timeNumber ++;d3.select('#time').html(this._timeNumber);},1000)}//开始游戏
    startGame() {this.initData();this.randomData();this.nextGraphNumber();this.initGraph();this.initTime();}
}

 

2. css 代码

* {padding: 0;margin: 0;
}
body {width: 480px;margin: 30px auto;
}
.svg-container {overflow: hidden;border: 5px solid rgba(0,0,0,0.2);width: 330px;position: relative;float: left;
}
#modal {position: absolute;top: 0px;background-color: white;border-bottom: 5px solid rgb(202,202,202);padding: 20px;width: 310px;text-align: center;z-index: 100;transition: 200ms linear;
}
#newGame {text-decoration: none;color: gray;font-size: 25px;cursor: pointer;
}
aside {position: relative;float: right;
}
aside .next {width: 100px;height: 60px;padding: 10px;border: 5px solid rgba(0,0,0,0.2);border-radius: 2px;margin-bottom: 10px;
}
aside .score {width: 100px;padding: 10px;border: 5px solid rgba(0,0,0,0.2);border-radius: 2px;color: gray;
}
aside .pause {color: gray;font-size: 12px;font-style: italic;padding-left: 3px;margin-top: 15px;
}
.row {stroke: lightgray;
}
.col {stroke: lightgray;
}

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 id="modal" class="active"><span id="newGame" onclick="newGame()">New Game</span></div></div><aside><div class="next"></div><div class="score"><table><tr><td>Level:</td><td id="level"></td></tr><tr><td>Score:</td><td id="score"></td></tr><tr><td>Time:</td><td id="time"></td></tr></table></div><div class="pause"><input type="checkbox" onclick="toggleGrid()"/> 网格</div></aside></div>
<script>
var tetris = new Tetris();function toggleGrid() {tetris.toggleGrid()
}
function newGame() {document.getElementById('modal').style.top = '-100px';tetris.startGame()
}
</script>
</body>
</html>

想预览或者下载demo的人请移步至原文

 

原文地址 http://www.bettersmile.cn

转载于:https://www.cnblogs.com/vadim-web/p/11492095.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/248141.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flask中路由系统以及蓝图的使用

一、Flask的路由系统 1.app.route()装饰器中的参数 methods:当前URL地址&#xff0c;允许访问的请求方式 app.route("/info", methods["GET", "POST"]) def student_info():stu_id int(request.args["id"])return f"Hello Old b…

js高级第五天

课程回顾&#xff1a; ​ 原型链&#xff1a;由原型构成链状结构&#xff0c;提供成员查找机制 ​ 继承&#xff1a;组合继承&#xff1a;构造函数和原型对象 ​ 属性&#xff1a;调用父构造函数的时候用call改变this指向 ​ 方法&#xff1a;父实例对象赋值给子原型对象&a…

d3.js 制作简单的贪吃蛇

d3.js是一个不错的可视化框架&#xff0c;同时对于操作dom也是十分方便的。今天我们使用d3.js配合es6的类来制作一个童年小游戏–贪吃蛇。话不多说先上图片。 1. js snaker类 class Snaker {constructor() {this._size 30;this._len 3;this._width 900;this._height 690;th…

js高级第六天

Q课程回顾&#xff1a; ​ 闭包&#xff1a;有权访问另外一个函数的局部变量的函数&#xff0c;作用&#xff1a;延伸变量使用范围 ​ mdn&#xff0c;w3c function fn1 () {var n 3;return function () {console.log(n);} }​ 递归&#xff1a;函数调用其本身 function f…

Chrome 75 lazy-loading

Chrome 75 & lazy-loading https://addyosmani.com/blog/lazy-loading/ https://chromestatus.com/feature/5645767347798016 Chrome 75 将默认启用延迟加载功能 自 Chrome 75 起&#xff0c;将原生支持图片的延迟加载&#xff0c;在代码中编写 <img loading"lazy&…

d3.js 实现烟花鲜果

今天在d3.js官网上看到了一个烟花的DEMO&#xff0c;是canvas制作的&#xff0c;于是我想用d3.js来实现它&#xff0c;js代码只有几行。好了废话不多说&#xff0c;先上图。 1 js 类 因为烟花要有下落的效果&#xff0c;所以里面用到了一些简单的数学和物理知识来模拟重力&…

阿里Sentinel控制台源码修改-对接Apollo规则持久化

改造背景 前面我们讲解了如何对接Apollo来持久化限流的规则&#xff0c;对接后可以直接通过Apollo的后台进行规则的修改&#xff0c;推送到各个客户端实时生效。 但还有一个问题就是Sentinel控制台没有对接Apollo&#xff0c;Sentinel控制台本来就可以修改限流的规则&#xff0…

Python学习(一)

一、版本&#xff1a; Python2.X /Python3.x 官方宣布2020 年 1 月 1 日&#xff0c; 停止 Python 2 的更新。 Python3.x不兼容Python2.x  二、安装&#xff08;以mac 为例&#xff09; MAC 系统一般都自带有 Python2.x版本 的环境&#xff0c;你也可以在链接 https://www.py…

jQuery—淘宝精品服饰案例

<body><div class"wrapper"><ul id"left"><li><a href"#">女靴</a></li><li><a href"#">雪地靴</a></li><li><a href"#">冬裙</a>&l…

Python机器学习实践:决策树判别汽车金融违约用户

文章发布于公号【数智物语】 &#xff08;ID&#xff1a;decision_engine&#xff09;&#xff0c;关注公号不错过每一篇干货。 转自 | 法纳斯特&#xff08;公众号ID:walker398&#xff09; 作者 | 小F 决策树呈树形结构&#xff0c;是一种基本的回归和分类方法。 决策树模型的…

jQuery—tab栏切换

<div class"tab"><div class"tab_list"><ul><li class"current">商品介绍</li><li>规格与包装</li><li>售后保障</li><li>商品评价&#xff08;50000&#xff09;</li><l…

操作系统原理之I/O设备管理(第六章上半部分)

一、I/O系统的组成 I/O系统不仅包括各种I/O设备&#xff0c;还包括与设备相连的设备控制器&#xff0c;有些系统还配备了专⻔⽤ 于输⼊/输出控制的专⽤计算机&#xff0c;即通道。此外&#xff0c;I/O系统要通过总线与CPU、内存相连。 I/O系统的结构&#xff1a; I/O设备的分类…

操作系统原理之I/O设备管理(第六章下半部分)

五、I/O软件原理 输入输出软件的总体目标是将软件组织成一种层次结构 低层软件用来屏蔽硬件的具体细节高层软件则主要是为用户提供一个简洁、规范的界面设备管理的4个层次&#xff1a; 用户层软件 -》向系统发出I/O请求&#xff0c;显示I/O操作的结果&#xff0c;提供⽤户与设备…

切换Debug/Release编译模式和Archive的作用

&#xfeff;在学这个之前&#xff0c;以为很难&#xff0c;也起不到什么作用&#xff0c;但是等真正运用到工程里面&#xff0c;才发现&#xff0c;这个能帮你省下很多工作量。 1&#xff0c;Debug和Release版本区别&#xff1f; 进行iOS开发&#xff0c;在Xcode调试程序时&am…

AFNetworking 对数据进行https ssl加密

参考来源&#xff1a;http://www.cnblogs.com/jys509/p/5001566.html 现在在工作中的工作需求&#xff1a;https请求验证证书一般来讲如果app用了web service , 我们需要防止数据嗅探来保证数据安全.通常的做法是用ssl来连接以防止数据抓包和嗅探其实这么做的话还是不够的 。…

数据库系统原理(第一章概述)

一、数据库基本概念 什么是数据&#xff1a;数据&#xff08;Data&#xff09;是描述事物的符号记录&#xff0c;是指利用物理符号记录下来的、 可以鉴别的信息。 数据是信息存在的一种形式&#xff0c;只有通过解释或处理的数据才能成为有用的信息。 什么是数据库&#xff1a;…

实验二:Linux下Xen环境的安装

实验名称&#xff1a; Linux下Xen环境的安装&#xff08;centOS7&#xff09; 实验环境&#xff1a; 本次实验基本是在centOS7的环境下完成&#xff0c;系统内核和系统版本如下&#xff1a; 实验要求&#xff1a; 为centOS7的环境下安装Xen的平台&#xff0c;能够正常使用Xen下…

IDEA写vue项目出现红色波浪线警告如何解决??

1.看图 2.希望对大家有帮助&#xff0c;只要修改了这个就可以&#xff0c;如有任何问题都可以留言&#xff0c;谢谢大家 2019-09-1923:54:11 作者&#xff1a;何秀好 转载于:https://www.cnblogs.com/itboxue/p/11553395.html

数据可视化(BI报表的开发)第一天

课程回顾&#xff1a; ​ jQuery事件注册&#xff1a; ​ $(元素).click(function () {}); ​ $(元素).on(‘click’, [后代元素], function () {}); ​ $(元素).one(‘click’, function () {}); ​ 解绑事件&#xff1a;off ​ 自动触发&#xff1a; ​ $(元素).click…

在Block中使用weakSelf与strongSelf的意义

在Block中使用weakSelf与strongSelf的意义 我们都会声明一个弱引用在block中使用, 目的就是防止循环引用, 那么weakSelf与strongSelf一起使用目的是什么呢? 首先先定义2个宏: #define YXWeakSelf(type) __weak typeof(type) weak##type type; #define StrongSelf(type) __…