Javascript编写2048小游戏_javascript技巧
去年2048很火, 本来我也没玩过, 同事说如果用JS写2048 只要100多行代码;
今天试了一下, 逻辑也不复杂, 主要是数据构造函数上的数据的各种操作, 然后通过重新渲染DOM实现界面的更新, 整体不复杂, JS,css,和HTML合起来就300多行;
界面的生成使用了underscore.js的template方法, 使用了jQuery,主要是DOM的选择和操作以及动画效果,事件的绑定只做了PC端的兼容,只绑定了keydown事件;
把代码放到github-page上, 通过点击这里查看 实例: 打开2048实例;
效果图如下:
所有的代码分为两大块,Data, View;
Data是构造函数, 会把数据构造出来, 数据会继承原型上的一些方法;
View是根据Data的实例生成视图,并绑定事件等, 我直接把事件认为是controller了,和View放在了一起, 没必要分开;
Data的结构如下:
/** * @desc 构造函数初始化 * */ init : function /** * @desc 生成了默认的数据地图 * @param void * */ generateData : function /** * @desc 随机一个block填充到数据里面 * @return void * */ generationBlock : function /** * @desc 获取随机数 2 或者是 4 * @return 2 || 4; * */ getRandom : function /** * @desc 获取data里面数据内容为空的位置 * @return {x:number, y:number} * */ getPosition : function /** * @desc 把数据里第y排, 第x列的设置, 默认为0, 也可以传值; * @param x, y * */ set : function /** * @desc 在二维数组的区间中水平方向是否全部为0 * @desc i明确了二维数组的位置, k为开始位置, j为结束为止 * */ no_block_horizontal : function no_block_vertica : function /** * @desc 往数据往左边移动,这个很重要 * */ moveLeft : function moveRight : function moveUp : function moveDown : function
有了数据模型,那么视图就简单了,主要是用底线库underscore的template方法配合数据生成html字符串,然后对界面进行重绘:
View的原型方法:
renderHTML : function //生成html字符串,然后放到界面中
init : function //构造函数初始化方法
bindEvents : function //给str绑定事件, 认为是控制器即可
因为原始的2048有方块的移动效果, 我们独立起来了一个服务(工具方法,这个工具方法会被View继承), 主要是负责界面中的方块的移动, getPost是给底线库用的, 在模板生成的过程中需要根据节点的位置动态生成横竖坐标,然后定位:
var util = { animateShowBlock : function() { setTimeout(function() { this.renderHTML(); }.bind(this),200); }, animateMoveBlock : function(prop) { $("#num"+prop.form.y+""+prop.form.x).animate({top:40*prop.to.y,left:40*prop.to.x},200); }, //底线库的模板中引用了这个方法; getPost : function(num) { return num*40 + "px"; } //这个应该算是服务; };
下面是全部的代码, 引用的JS使用了CDN,可以直接打开看看:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <script src="http://cdn.bootcss.com/underscore.js/1.8.3/underscore-min.js"></script> <script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.js"></script> <style> #g{ position: relative; } .block,.num-block{ position: absolute; width: 40px; height: 40px; line-height: 40px; text-align: center; border-radius: 4px; } .block{ border:1px solid #eee; box-sizing: border-box; } .num-block{ color:#27AE60; font-weight: bold; } </style> <div class="container"> <div class="row"> <div id="g"> </div> </div> </div> <script id="tpl" type="text/template"> <% for(var i=0; i<data.length; i++) {%> <!--生成背景块元素---> <% for(var j=0; j< data[i].length; j++ ) { %> <div id="<%=i%><%=j%>" class="block" style="left:<%=util.getPost(j)%>;top:<%=util.getPost(i)%>" data-x="<%=j%>" data-y="<%=i%>" data-info='{"x":<%=[j]%>,"y":<%=[i]%>}'> </div> <% } %> <!--生成数字块元素---> <% for(var j=0; j< data[i].length; j++ ) { %> <!--如果数据模型里面的值为0,那么不显示这个数据的div---> <% if ( 0!==data[i][j] ) {%> <div id="num<%=i%><%=j%>" class="num-block" style="left:<%=util.getPost(j)%>;top:<%=util.getPost(i)%>" > <%=data[i][j]%> </div> <% } %> <% } %> <% } %> </script> <script> var Data = function() { this.init(); }; $.extend(Data.prototype, { /** * @desc 构造函数初始化 * */ init : function() { this.generateData(); }, /** * @desc 生成了默认的数据地图 * @param void * */ generateData : function() { var data = []; for(var i=0; i<4; i++) { data[i] = data[i] || []; for(var j=0; j<4; j++) { data[i][j] = 0; }; }; this.map = data; }, /** * @desc 随机一个block填充到数据里面 * @return void * */ generationBlock : function() { var data = this.getRandom(); var position = this.getPosition(); this.set( position.x, position.y, data) }, /** * @desc 获取随机数 2 或者是 4 * @return 2 || 4; * */ getRandom : function() { return Math.random()>0.5 ? 2 : 4; }, /** * @desc 获取data里面数据内容为空的位置 * @return {x:number, y:number} * */ getPosition : function() { var data = this.map; var arr = []; for(var i=0; i<data.length; i++ ) { for(var j=0; j< data[i].length; j++ ) { if( data[i][j] === 0) { arr.push({x:j, y:i}); }; }; }; return arr[ Math.floor( Math.random()*arr.length ) ]; }, /** * @desc 把数据里第y排, 第x列的设置, 默认为0, 也可以传值; * @param x, y * */ set : function(x,y ,arg) { this.map[y][x] = arg || 0; }, /** * @desc 在二维数组的区间中水平方向是否全部为0 * @desc i明确了二维数组的位置, k为开始位置, j为结束为止 * */ no_block_horizontal: function(i, k, j) { k++; for( ;k<j; k++) { if(this.map[i][k] !== 0) return false; }; return true; }, //和上面一个方法一样,检测的方向是竖排; no_block_vertical : function(i, k, j) { var data = this.map; k++; for(; k<j; k++) { if(data[k][i] !== 0) { return false; }; }; return true; }, /** * @desc 往左边移动 * */ moveLeft : function() { /* * 往左边移动; * 从上到下, 从左到右, 循环; * 从0开始继续循环到当前的元素 ,如果左侧的是0,而且之间的空格全部为0 , 那么往这边移, * 如果左边的和当前的值一样, 而且之间的空格值全部为0, 就把当前的值和最左边的值相加,赋值给最左边的值; * */ var data = this.map; var result = []; for(var i=0; i<data.length; i++ ) { for(var j=1; j<data[i].length; j++) { if (data[i][j] != 0) { for (var k = 0; k < j; k++) { //当前的是data[i][j], 如果最左边的是0, 而且之间的全部是0 if (data[i][k] === 0 && this.no_block_horizontal(i, k, j)) { result.push( {form : {y:i,x:j}, to :{y:i,x:k}} ); data[i][k] = data[i][j]; data[i][j] = 0; //加了continue是因为,当前的元素已经移动到了初始的位置,之间的循环我们根本不需要走了 break; }else if(data[i][j]!==0 && data[i][j] === data[i][k] && this.no_block_horizontal(i, k, j)){ result.push( {form : {y:i,x:j}, to :{y:i,x:k}} ); data[i][k] += data[i][j]; data[i][j] = 0; break; }; }; }; }; }; return result; }, moveRight : function() { var result = []; var data = this.map; for(var i=0; i<data.length; i++ ) { for(var j=data[i].length-2; j>=0; j--) { if (data[i][j] != 0) { for (var k = data[i].length-1; k>j; k--) { //当前的是data[i][j], 如果最左边的是0, 而且之间的全部是0 if (data[i][k] === 0 && this.no_block_horizontal(i, k, j)) { result.push( {form : {y:i,x:j}, to :{y:i,x:k}} ); data[i][k] = data[i][j]; data[i][j] = 0; break; }else if(data[i][k]!==0 && data[i][j] === data[i][k] && this.no_block_horizontal(i, j, k)){ result.push( {form : {y:i,x:j}, to :{y:i,x:k}} ); data[i][k] += data[i][j]; data[i][j] = 0; break; }; }; }; }; }; return result; }, moveUp : function() { var data = this.map; var result = []; // 循环要检测的长度 for(var i=0; i<data[0].length; i++ ) { // 循环要检测的高度 for(var j=1; j<data.length; j++) { if (data[j][i] != 0) { //x是确定的, 循环y方向; for (var k = 0; k<j ; k++) { //当前的是data[j][i], 如果最上面的是0, 而且之间的全部是0 if (data[k][i] === 0 && this.no_block_vertical(i, k, j)) { result.push( {form : {y:j,x:i}, to :{y:k,x:i}} ); data[k][i] = data[j][i]; data[j][i] = 0; break; }else if(data[j][i]!==0 && data[k][i] === data[j][i] && this.no_block_vertical(i, k, j)){ result.push( {form : {y:j,x:i}, to :{y:k,x:i}} ); data[k][i] += data[j][i]; data[j][i] = 0; break; }; }; }; }; }; return result; }, moveDown : function() { var data = this.map; var result = []; // 循环要检测的长度 for(var i=0; i<data[0].length; i++ ) { // 循环要检测的高度 for(var j=data.length - 1; j>=0 ; j--) { if (data[j][i] != 0) { //x是确定的, 循环y方向; for (var k = data.length-1; k>j ; k--) { if (data[k][i] === 0 && this.no_block_vertical(i, k, j)) { result.push( {form : {y:j,x:i}, to :{y:k,x:i}} ); data[k][i] = data[j][i]; data[j][i] = 0; break; }else if(data[k][i]!==0 && data[k][i] === data[j][i] && this.no_block_vertical(i, j, k)){ result.push( {form : {y:j,x:i}, to :{y:k,x:i}} ); data[k][i] += data[j][i]; data[j][i] = 0; break; }; }; }; }; }; return result; } }); var util = { animateShowBlock : function() { setTimeout(function() { this.renderHTML(); }.bind(this),200); }, animateMoveBlock : function(prop) { $("#num"+prop.form.y+""+prop.form.x).animate({top:40*prop.to.y,left:40*prop.to.x},200); }, //底线库的模板中引用了这个方法; getPost : function(num) { return num*40 + "px"; } //这个应该算是服务; }; var View = function(data) { this.data = data.data; this.el = data.el; this.renderHTML(); this.init(); }; $.extend(View.prototype, { renderHTML : function() { var str = _.template( document.getElementById("tpl").innerHTML )( {data : this.data.map} ); this.el.innerHTML = str; }, init : function() { this.bindEvents(); }, bindEvents : function() { $(document).keydown(function(ev){ var animationArray = []; switch(ev.keyCode) { case 37: animationArray = this.data.moveLeft(); break; case 38 : animationArray = this.data.moveUp(); break; case 39 : animationArray = this.data.moveRight(); break; case 40 : animationArray = this.data.moveDown(); break; }; if( animationArray ) { for(var i=0; i<animationArray.length; i++ ) { var prop = animationArray[i]; this.animateMoveBlock(prop); }; }; this.data.generationBlock(); this.animateShowBlock(); }.bind(this)); } }); $(function() { var data = new Data(); //随机生成两个节点; data.generationBlock(); data.generationBlock(); //生成视图 var view = new View({ data :data, el : document.getElementById("g") }); //继承工具方法, 主要是动画效果的继承; $.extend( true, view, util ); //显示界面 view.renderHTML(); }); </script> </body> </html>
以上所述就是本文的全部内容了,希望大家能够喜欢。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用于在DOM树中插入一个新的节点。这个方法需要两个参数:要插入的新节点和参考节点(即新节点将要被插入的位置的节点)。

JavaScript是一种广泛应用于Web开发的编程语言,而WebSocket则是一种用于实时通信的网络协议。结合二者的强大功能,我们可以打造一个高效的实时图像处理系统。本文将介绍如何利用JavaScript和WebSocket来实现这个系统,并提供具体的代码示例。首先,我们需要明确实时图像处理系统的需求和目标。假设我们有一个摄像头设备,可以采集实时的图像数
