前端纯原生代码实现2048
时间:2023-04-02 20:08:51
HTML
前言为什么没事做一个2048?不是360前端之星项目(2018春招实习生)让我做的。然后花了几天时间做了一个2048的小游戏,兼容PC端和部分移动端(设备有限,部分手机浏览器实在是不兼容或者实在不想兼容)。只是给大家看看。github地址:https://github.com/GDUTxxZ/20...在线预览:http://47.94.199.75/index.html(这个网址暂时有效。。不知道点什么实验工作在后面。。)当我完成了游戏介绍给朋友们看后,才发现并不是每个人都玩过这个游戏。简单说一下游戏的内容。获胜条件:拼出一个2048个方格失败条件:当前没有可用的方格,且没有一个方格可以与相邻的方格合并代码结构index.html:
游戏失败#bg是背景图,也就是一个空的灰色方块,因为当方块移动的时候,下面的空白是不能露出来的。#main是实体,也就是我们在游戏中看到的包含数字的方块。#alert是提示框,开始时显示:none,当游戏获胜或结束时显示:block#alertspan失败消息或胜利消息css主要是动画元素的设置:base.css#main。item{transition:all.3sease-out;-moz-transition:所有.3s缓出;/*Firefox4*/-webkit-transition:all.3sease-out;/*Safari和Chrome*/-o-transition:all.3sease-out;/*Opera*/left:0px;top:0px;}js主要由两部分组成,util.js负责一些外围功能的处理(重要的是移动滑动事件的封装),2048.js是页面util的整体逻辑.js//移动滑动事件的封装consttouchManager=(function(){letstart=[]letend=[]lettimeStamp=0letmanager={}manager.touchstart=function(event){//记录开始位置event.stopPropagation()timeStamp=event.timeStamp//获取点击时间lettarget=event.targetTouches[0]start=[target.pageX,target.pageY]end=[target.pageX,target.pageY]console.log('start')}manager.touchmove=function(event){//记录移动位置event.stopPropagation()event.preventDefault()lettarget=event.targetTouches[0]end=[target.pageX,target.pageY]console.log('move')}manager.touchend=function(event){//处理起始位置和移动位置以给出滑动方向event.stopPropagation()event.preventDefault()constabs=Math.abslettime=event.timeStamp-timeStamp//获取滑动操作使用的时间letmoveX=end[0]-start[0]letmoveY=end[1]-start[1]if(time>500||(abs(moveX)<50&&abs(moveY)<50)){//如果移动距离不够或时间太长,则不认为是滑动returnfalse}else{if(abs(moveX)>=abs(moveY)){//水平移动距离较长console.log(moveX)返回moveX>0?'right':'left'}else{//更长的垂直运动console.log(moveY)returnmoveY>0?'down':'up'}}}returnmanager})()2048.js主要由以下数据结构和函数组成:数据结构:letdata=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]//初始化letemptyList=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]//当前没有网格元素constcontainer=document.querySelector('#main')//操作实体constbg=document.querySelector('#bg')//背景面板constalert=document.querySelector('#alert')//警告框constalertText=alert.querySelector('span')//警告框中的文本容器constbaseSize=parseInt(getDomStyle(container).width)//基础画板的sizeletgameOver=false//标志游戏是否结束constanimateTime=300//单位ms,动画时长这个地方保存了很多dom元素的引用,为了降低性能以后操作获取dom的消耗,还有如下函数:main//主程序resize(el)//调整元素的宽高一致createElement()//在emptyList中找1-2个下标,给data添加一个新元素,值为{2,4}paint(el,data)//使用el中的data绘制每一个gridinanimate(move,arrow)//传入一个move动态队列,移动方向'left'表示水平,'top'表示垂直movemoveHandle={...}//封装计算移动结果的函数然后从主程序看函数流程:functionmain(){//主程序//调整背景和实体宽度和heightresize(container)resize(bg)//初始化背景和实体元素paint(bg,data)//创建1-2个初始元素createNewElement()paint(container,data)//绑定事件监听器addEvent(window,'keydown',function(event){//按键监听if(gameOver){return}letarrow=keyCodeMap[event.keyCode]switch(arrow){case'left':case'up':case'right':case'down':{moveHandle.move(arrow)break}}})addEvent(alert.querySelector('button'),'click',replay)//再次播放addEvent(container,'touchstart',touchManager.touchstart)addEvent(container,'touchmove',touchManager.touchmove)addEvent(容器,'touchend',函数(事件){letarrow=touchManager.touchend(event)if(arrow){moveHandle.move(arrow)}})}即:1.初始化2.绑定事件监听然后如何计算移动结果,下面使用左滑以计算(moveHandle.moveleft)为例)//获取当前数据的副本letmove=[]//块移动队列emptyList=[]for(leti=0;i<4;i++){//逐行处理letnewList=[]//newlineletoldList=data[i]for(letj=0;j<4;j++){//查找所有非零单元letvalue=newData[i][j]if(value!==0){newList.push(value)}}if(newList.length>1){//合并相似项for(letj=0,len=newList.length;jitem!==0)//过滤掉上一步生成的0}for(letj=newList.length;j<4;j++){//补全list末尾的0emptyList.push(i//j是旧元素的位置,k是移动到的位置if(newList[k]===0){//如果没有移动的位置break}elseif(oldList[j]===newList[k]){//j移动到位置kif(j!==k){move.push({start:[i,j],end:[i,k]})}k++}elseif(oldList[j]===newList[k]/2){//两个元素合成位置k的元素move.push({start:[i,j],end:[i,k]})if(tag){k++}tag=!tag}}}return{newData:newData,move:move}}该函数最终输出的是move块的移动队列newData的计算数据,形式为[{start:[x1,y1],end:[x2,y2]},...]那么如何使用这个计算结果,看moveHandle.move。(moveHandle中有三个私有变量,moving锁住手柄,防止用户在动画过程中再次滑动,win是否赢,失败则输)move:function(arrow){//arrow=directionofmovementif(this.movi??ng){//如果动画正在进行,返回移动失败returnfalse}letresult=this['move'+arrow]()//获取移动计算的结果letnewData=result.newData//数据移动后的矩阵letmove=result.move//移动元素列表//根据移动元素列表判断操作是否有效if(move.length===0){//没有要移动的东西元素,无效console.log('thismoveisinvalid')returnfalse}//做0.3秒动画data=newData//修改全局数据矩阵createNewElement()//创建一个新元素//判断结果游戏this.win=isWin(newData)if(!this.win){this.lost=isLost(newData,emptyList)}this.movi??ng=true//锁定事件处理程序setTimeout((function(self){animate(move,arrow)returnfunction(){//经过足够的时间self.movi??ng=false//终止动画paint(container,data)//重绘视图//确定游戏结果if(self.win){//赢得游戏win()}elseif(self.lost){lost()}}})(this),animateTime)}我觉得我还有很多注解,这个分享应该还是能看懂的。欢迎在评论区留言讨论。如果您发现任何错误,请尽可能多地告诉我。已知的bug有:1、微信自带浏览器的转码问题:这是我懒得补一个域名,为了安全会转码,玩不了。它不会被修复。.只是一个小玩具。2、ios长按会选择文字,无法取消:这个问题我已经做了一些修复,但是我没有办法重现这个问题,就没有再处理了。系统行为:我想不出办法,如果谁有办法请告诉我,谢谢。