看了人家视频上写的贪吃蛇,瞬间觉得自己low爆了。。。。就学他写了三遍,以我的记性,觉得还是有必要记录下过程,以便记忆。
先看下截图:
学写前端原生贪吃蛇-LMLPHP
首先html分析:
注:下列的id是用在js里的,class是用在css里的

 <div class="startPage" id="startPage">     <!-- 这是开始页的大页面 -->
        <div class="startBtn" id="startBtn"></div>  <!-- 载在大页面上的开始按钮键 -->
    </div>
    <div class="wrapper">          <!-- 开始后的大页面 -->
        <div class="left-side">         <!-- 将大页面分出一个左边,用来承载暂停和计分的按键 -->
            <div class="header">      <!-- 在这个左边的开头写个用来载计分的框 -->
                <div class="score">     <!-- 显示“分数” -->
                    分数:
                    <span id="score"></span>    <!-- 用在之后js里的动态计分操作 -->
                </div>
            </div>
            <img src="img/zanting.png" id="startP" alt="">      <!-- 显示暂停按钮,这个按钮用图片代替 -->
        </div>
        <div class="main">     <!-- 大页面中的主页面,和左边的区分下 -->
            <div class="content" id="content"></div>    <!-- 游戏区域,在js里控制 -->
        </div>
    </div>
    <div class="lose" id="lose">       <!-- 游戏结束的大页面 -->
        <div class="con">         <!-- 游戏结束后的计分清单 -->
            <span class="loseScore" id="loseScore">       <!-- js动态计分 -->
            </span>
            <div class="close" id="close"></div>        <!-- 游戏结束计分上的关闭按钮 -->
        </div>
    </div>

上述是贪吃蛇的全部html代码,将贪吃蛇游戏的大致界面划分出来。
接下来是css代码:

*{                             /* css的书写习惯,先将所有的margin和padding的默认值都去掉 */
    margin: 0;
    padding: 0;
}

.startPage{                 /* 开始游戏的大页面设置 */
    width: 100%;
    height: 640px;                  /* 游戏界面高度,这是我按自己电脑浏览器大小设置的 */
    position: absolute;                 /* 给它个绝对定位 */
    z-index: 999;                        /* 显示在界面最前头 */

}

.startBtn{                          /* 游戏开始按键的设置 */
    width: 200px;                 /* 设置按键图片的宽和高 */
    height: 50px;
    position: absolute;          /* 从这到margin:auto,这几行放在一起,表示居中在大页面的正中间 */
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin:auto;
    background-image: url(img/begin.png);       /* 把开始按键的图片放入 */
    background-size: 100% 100%;                      /* 将图片平摊开 */
    cursor: pointer;                     /* 鼠标划入时,箭头变成小手 */

}


.wrapper{                    /* 游戏开始后的大页面 */
    width: 100%;
    height: 640px;             /* 同上含义 */
    background-image: url('img/bj1.png');     /* 放入游戏背景图片 */
    background-size: 100% 100%;       /* 平摊 */
    position: relative;         /* 相对定位 */
    top: 0px;
    left: 0px;
}

.left-side{           /* 在大页面上划分出的左边部分 */
    width: 14%;         /* 占大页面的14%宽度 */
    height: 640px;       /* 高度 */
    position: absolute;      /* 绝对定位 */
    top: 0;
    left: 0;
}

.left-side img{      /* 显示暂停按键图片的设置 */
    width: 120px;     /* 设置图片的宽高 */
    height: 40px;
    position: absolute;      /* 绝对定位 */
    left: 15%;         /* 该图片在左边划分框中,距离左边距15% */
    top: 100px;        /* 图片距离上边距100px */

}

.header{                  /* 用来承载计分的框 */
    width: 100%;
    height: 40px;
    margin-top: 30px;        /* 外边距30px */
    position: absolute;        /* 绝对定位 */
}

.score{            /* 计分数字显示 */
    width: 100px;
    height: 40px;
    font-size: 18px;
    color: #fff;      /* 字体白色 */
    position: absolute;       /* 基于上个absolute的中心定位 */
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin:auto;
}

.main{        /* 在大页面中划分出的另一个部分 */
    position: absolute;       /* 绝对定位 */
    left: 15%;         /* 距离大页面左边据15% */
    width: 52%;         /* 占大页面的宽52% */
    height: 640px;         /* 值含义同上 */
    border: 1px solid black;      /* 边框,作为标识,可去除 */
}

.content{     /* 游戏边框,用来表示蛇不能出去的部分 */
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin:auto;      /* 从position到这表示基于父级定位的中心定位 */
    width: 525px;      /* 边框的宽高 */
    height: 525px;
    border: 13px solid rgb(2, 32, 7);      /* 边框框 */
}

.lose{             /* 表示游戏结束后的出现的一个大页面 */
    width: 100%;
    height: 640px;
    position: absolute;
    top: 0;
    left: 0;
    display: none;       /* 先设置不显示,之后在js中修改 */
}

.con{      /* 表示游戏结束后出现的结束图片 */
    width: 340px;       /* 为图片设置宽高 */
    height: 160px;
    background-image:url('img/over.png');     /* 加入图片 */
    background-size: 100% 100%;         /* 平摊图片 */
    position: absolute;            /* 同上 */
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin:auto;
    border-radius: 15% 15%;       /* 图片的四个角设置圆度 */
}

.loseScore{       /* 在js中调用,用来显示最后的计分结果 */
    font-size: 24px;     /* 字体大小24px */
    color: #fff;
    position: absolute;
    left: 130px;         /* 计分数字在游戏结束图片上找到合适的位置存放 ,这是按自己的图片设置的*/
    top: 80px;
}

.close{          /* 设置游戏结束右上角的关闭按钮 */
    background-image: url('img/shanchu.png');      /* 插入关闭按钮图片 */
    background-size: 100% 100%;      /* 平摊图片 */
    width: 35px;        /* 设置关闭按钮的宽高 */
    height: 35px;
    position: absolute;      /* 绝对定位 */
    top: 0px;
    right: 0px;        /* 图片放到右上角 */
}


还有部分css代码,在js代码编辑的时候一同写出,现在上述的css算是将贪吃蛇游戏的页面背景设置完毕。
接下来js代码:

var content = document.getElementById('content');        //将html中有id = content的元素提取出来

function init(){                //设置初始时的所有默认值
    this.foodW = 20;           //表示食物的宽
    this.foodH = 20;               //高
    this.foodX = 0;                    //表示食物的x坐标位置
    this.foodY = 0;                      //y坐标位置

    this.snakeW = 0;              //蛇的宽高
    this.snakeH = 0;
    this.snakeBody = [[4,1,'head'],[3,1,'body'],[2,1,'body']];        //表示三节蛇身(包括蛇头),用数组表示

    this.mapH = parseInt(getComputedStyle(content).height);     //表示游戏界面的宽高,用getComputedStyle(content).height来提取出content里的height属性值
    this.mapW = parseInt(getComputedStyle(content).width);       //同上含义
    this.mapDiv = content;                      //表示界面整体

    this.direct = 'right';               //初始时蛇的方向
    this.up = true;               // 表示向上蛇可动
    this.down = true;          //向下可动
    this.right = false;         //向右边不可动
    this.left = false;          //向左边不可动
//上述表示的具体意思是,当蛇的运动方向为右时,不能操作蛇的左右运动,只能操作上下
    this.score = 0;         //初始计分为0
    bindEvent();           //触发bindEvent()函数
}

设置完初始值后,开始设置蛇和食物的初始状态

function food(){            //食物的设置
    var food = document.createElement('div');         //生成一个div的食物food
    food.style.width = this.foodW + 'px';                   //设置食物的宽高,等于上述函数中已经设置好的默认食物宽高
    food.style.height = this.foodH + 'px';             //同上
    food.style.position = 'absolute';                   //设置食物的位置为绝对定位
    this.foodX = Math.floor(Math.random()*this.mapW/20);        //食物的x坐标的随机位置,表示0到(游戏界面的宽除以食物自己的宽)的随机数
    this.foodY = Math.floor(Math.random()*this.mapH/20);          //食物的y坐标的随机位置,同上
    food.style.top = this.foodY * 20 + 'px';         //表示食物的上边距距离为食物的y坐标乘以自己的高
    food.style.left = this.foodX * 20 + 'px';     //同上
    this.mapDiv.appendChild(food).setAttribute('class','food');      //给每个食物div都设置上名为food的class,再到css中设置食物的大小和载入图片
}

function snake(){         //蛇的设置
    for(var i = 0;i < this.snakeBody.length;i ++){          //用for循环,i小于蛇身的长度,(初始时的length为3)
        var snake = document.createElement('div');      //生成一个div的蛇snake
        snake.style.width = this.snakeW + 20 + 'px';         //设置生成的蛇的宽高
        snake.style.height = this.snakeH + 20 +  "px";
        snake.style.position = 'absolute';        //设置蛇的绝对位置
        snake.style.top = this.snakeBody[i][1] * 20 + 'px';       //设置每节蛇的距离上边距的位置
        snake.style.left = this.snakeBody[i][0] * 20 + 'px';        //设置每节蛇的距离左边距的位置
        snake.classList.add(this.snakeBody[i][2]);              //给每节蛇添加class,这里的class为上述的snakeBody中设置的第3个位置上的‘head’和’body‘,因此又需要对head和body设置css代码,用来区分蛇头和蛇身
        this.mapDiv.appendChild(snake).classList.add('snake');      //在html中添加上snake的div,并加上class = ‘snake’

        switch(this.direct){         //蛇头的转向(注:由于我找的蛇头的方向默认为右边)
            case 'right':         //由于默认图片蛇头向右所以不需要改变操作
                break;
            case 'left' :        //将蛇头的方向转向左边
                snake.style.transform = 'rotate(180deg)';
                break;
            case 'up' :          //将蛇头方向旋转到上边
                snake.style.transform = 'rotate(270deg)';
                break;
            case 'down' :         //将蛇头方向旋转到下方
                snake.style.transform = 'rotate(90deg)';
                break;
            default :
                break;
        }
    }
}

食物和蛇的css代码:

.food{
    background-image: url('img/pg.png');        //加入食物的图片
    background-size: 100% 100%;
}

.head{
    background-image: url('img/s.png');           //加入蛇头的图片
    background-size: 100% 100%;
}

.body{
    background-image: url('img/she.png');       //加入蛇身的图片
    background-size: 100% 100%;
}

设置完蛇和食物的状态后,设置游戏开始时的界面:

var startPage = document.getElementById('startPage');   //从html中获取到id = startPage的元素节点,startPage表示开始游戏的按钮
var startP = document.getElementById('startP');     //同上,startP表示暂停的按钮

function startGame(){           //游戏开始
    startPage.style.display = 'none';        // 将该节点设置为不显示
    startP.style.display = 'block';            //使该节点显示
    food();     //调用food函数
    snake();     //调用snake函数
}

接下来设置蛇的运动状态js:

var scoreBox = document.getElementById('score');  //获取用于计分的节点
function move(){
    for(var i = this.snakeBody.length-1;i > 0;i --){     //for循环,表示蛇运动时,后一节蛇的位置是前一节蛇之前所在的位置
        this.snakeBody[i][0] = this.snakeBody[i-1][0];
        this.snakeBody[i][1] = this.snakeBody[i-1][1];
    }
    switch(this.direct){    //表示蛇头的转动位置情况
        case 'right' :
            this.snakeBody[0][0] +=1;         //右边时,蛇头的x位置加1
            break;
        case 'left' :
            this.snakeBody[0][0] -=1;                 //左边时,蛇头的x位置减1,
            break;
        case 'up' :
            this.snakeBody[0][1] -=1;            //上边时,蛇头的y位置减1
            break;
        case 'down' :
            this.snakeBody[0][1] +=1;           //下边时,蛇头的y位置加1
        default:
            break;
    }
    removeClass('snake');              //删除class = ‘snake’的节点
    snake();      //重新调用snake函数
    if(this.snakeBody[0][0] == this.foodX && this.snakeBody[0][1] == this.foodY){      //当蛇吃到食物时
        var snakeEndX = this.snakeBody[this.snakeBody.length-1][0]; //表示吃到食物后,蛇尾的x坐标为蛇的长-1
        var snakeEndY = this.snakeBody[this.snakeBody.length-1][1];    //同上
        switch(this.direct){
            case 'right':
                this.snakeBody.push([snakeEndX ,snakeEndY,'body']);    //蛇头向右时,蛇尾的x位置需要+1
                break;
            case 'left':
                this.snakeBody.push([snakeEndX ,snakeEndY,'body']);   //蛇头向左时,蛇尾的x位置需要-1
                break;
            case 'up':
                this.snakeBody.push([snakeEndX,snakeEndY ,'body']);   //蛇头向上时,蛇尾的y位置需要-1
                break;
            case 'down':
                this.snakeBody.push([snakeEndX,snakeEndY ,'body']);   //蛇头向下时,蛇尾的y位置需要+1
                break;
            default:
                break;
        }        //注:当蛇吃到食物时,添加的尾巴会在原来的尾巴的位置上加上,直到蛇头离开食物的位置,此时的尾巴就会变成真正的尾巴
        this.score += 1;    //吃到食物,分数+1
        scoreBox.innerHTML = this.score;    //使html中的id = ‘scoreBox’的节点上加上此时的分数
        removeClass('food');        //蛇吃到食物后,所吃的食物消失
        food();     //又重新出现新的食物的位置
    }

    if(this.snakeBody[0][0] < 0 || this.snakeBody[0][0] > this.mapW/20){   //当蛇头x碰到边界时,游戏结束
        reloadGame();    //结束游戏,重新加载游戏
    }
    if(this.snakeBody[0][1] < 0 || this.snakeBody[0][1] > this.mapH/20){    //当蛇头y碰到边界时,游戏结束
        reloadGame();   //结束游戏,重新加载游戏
    }
    var snakeHX = this.snakeBody[0][0];     //得到蛇头x
    var snakeHY = this.snakeBody[0][1];    //得到蛇头y
   for(var i = 1;i < this.snakeBody.length;i++){        //当蛇头碰到自己的身体时,游戏结束
    if(snakeHX == snakeBody[i][0] && snakeHY == snakeBody[i][1]){
        reloadGame();
    }
   }
}

接下来是删除节点的函数:

function removeClass(className){      //给一个class类名
    var ele = document.getElementsByClassName(className);   //用ele得到html中有该class类名的节点
    while(ele.length > 0){    //当节点长度大于0时,即存在该节点
        ele[0].parentNode.removeChild(ele[0]);    //删除该节点
    }
}

然后是重新加载游戏的js代码:

var loseScore = document.getElementById('loseScore');
function reloadGame(){
    removeClass('snake');      //删除class = ‘snake’的节点,即删除蛇
    removeClass('food');        //删除食物,同上
    clearInterval(snakeMove);      //清除snakeMove每隔时间间隔长度运动
    this.snakeBody = [[4,1,'head'],[3,1,'body'],[2,1,'body']];    //再添加上默认初始的蛇身坐标
    this.direct = 'right';        //初始方向
    this.right = false;
    this.left = false;
    this.up = true;
    this.down = true;
    lose.style.display = 'block';       //使id = ‘lose’的节点出现,即出现游戏结束后的图片
    loseScore.innerHTML = this.score;     //记录游戏结束后的最终得分
    this.score = 0;     //记录后,清0

    scoreBox.innerHTML = this.score;      //同时将游戏开始时的动态积分也清0
    startGameBool = true;
    startPauseBool = true;
    startP.setAttribute('src','./img/begin.png');      //改变id = ‘startP’节点的图片内容
}

关于操作蛇时的上下左右按键设置:

function setDirect(code){   //给该函数一个表示对应按键数字
    switch(code){
        case 37:       //表示左,方向为左时,上下的按键可触发但是左右的按键不可以触发
            if(this.left){
                this.direct = 'left';
                this.left = false;
                this.right = false;
                this.up = true;
                this.down = true;
            }
            break;
        case 38:      //上  ,同上
            if(this.up){
                this.direct = 'up';
                this.left = true;
                this.right = true;
                this.up = false;
                this.down = false;
            }
            break;
        case 39:        //右 ,同上
            if(this.right){
                this.direct = 'right';
                this.left = false;
                this.right = false;
                this.up= true;
                this.down = true;
            }
            break;
        case 40:      //下 ,同上
            if(this.down){
                this.direct = 'down';
                this.left = true;
                this.right = true;
                this.up = false;
                this.down = false;
            }
            break;
        default:
            break;
    }
}

接下来是关于游戏的开始和暂停的设置:

var startGameBool = true;       //表示该钥匙是开着时
var startPauseBool = true;
var snakeMove;
var speed = 200;

function startAndPush(){
    if(startPauseBool){         //关于暂停的钥匙
        if(startGameBool){        //关于游戏刚开启时的钥匙
            startGame();       //游戏开始,启动游戏开始的界面
            startGameBool = false;      //游戏开始后,不需要再出现这个过程,将该钥匙锁住
        }
        startP.setAttribute('src','./img/kk.png');    //为html中的id = ‘startP’设置插入图片
        document.onkeydown = function(e){       //触发按键发生的时间
            var code = e.keyCode;       //得到所触发按键所表示的数字
            setDirect(code);       //调用上述的按键方向函数
        }
        snakeMove = setInterval(function(){      表示每隔speed时间启用一次
            move();         调用move函数,表示每隔speed时间,执行一次move函数事件
        },speed);
        startPauseBool = false;         //执行过后,需要上锁,防止该事件在游戏过程中被启用出错


    }else{         //当钥匙关闭时,执行的事件
        startP.setAttribute('src','./img/zanting.png');      //改变id = startP的节点中的图片内容
        clearInterval(snakeMove);        //清除snakeMove每隔speed时间的执行
        document.onkeydown = function(e){       //表示禁止按键启用
            e.returnValue = false;
            return false;
        }
        startPauseBool = true;       //执行上述事件后,将锁打开
    }
}

之后是关于主体启用键盘按键和鼠标按键事件:

var lose = document.getElementById('lose');
var close = document.getElementById('close');
var startBtn = document.getElementById('startBtn');

function bindEvent(){
    document.onkeydown = function(e){      //同上述描述,是按键的数字获取和启用方向事件
        var code = e.keyCode;
        setDirect(code)
    }
    close.onclick = function(){       //表示游戏结束后右上角的关闭按键的触发事件
        lose.style.display = 'none';       //使游戏结束界面不显示
    }
    startBtn.onclick = function(){    //表示刚启动游戏时的开始按钮
        startAndPush();
    }
    startP.onclick = function(){         //表示游戏中的暂停和解除暂停的鼠标按键事件
        startAndPush();
    }
}

上述就是所有的js代码。。。。
看下成果图:学写前端原生贪吃蛇-LMLPHP
学写前端原生贪吃蛇-LMLPHP
注:上述代码如有解释错误的,请大佬帮忙改善,谢谢,有不清楚的也可以留言,本人会尽量及时回复的

11-21 17:53