单文件html,直接复制可用。
可调节速度和随机大小食物。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Snake</title>
<style>
.text{
width: 100%;
line-height: 30px;
height: 30px;
font-size: 16pt;
text-align: center;
}
canvas{
display: block;
margin: 30px auto;
border: solid;
border-width: 1px;
border-color:rgb(51, 51, 51);
box-shadow: 0 0 12px 2px rgba(51, 51, 51, 0.8);
}
.slider-contain{
text-align: center;
position: relative;
}
.slider-bar{
display: inline-block;
height: 8px;
width: 300px;
background-color: rgb(218, 218, 218);
border: solid;
border-color: rgb(218, 218, 218);
border-width: 1px;
box-shadow: 0 0 8px 2px rgba(97, 97, 97, 0.8);
}
.slider-block{
position: absolute;
height: 23px;
width: 10px;
background-color: rgb(8, 85, 8);
margin-top: -7px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="text" id="score">0</div>
<canvas id="canvas"></canvas>
<div class="slider-contain">
<abbr title="拖动调节速度">
<div class="slider-bar">
<div class="slider-block"></div>
</div>
</abbr>
</div>
<br><br>
<div class="text">
上下左右控制方向
</div>
<div class="text">
F5重新开始|空格暂停/继续
</div>
</body>
<script>
const direction={
UP:0,
RIGHT:1,
DOWN:2,
LEFT:3
}
var snake={
color:'#04573D',
headColor:'#211E1E',
size:10,
length:3,
direction:direction.RIGHT,
speed:100,// 200ms/move 1 time 200-50
body:[
{x:25,y:145},
{x:15,y:145},
{x:5,y:145}
],//每一节身体的位置
tail:[]//记录所有的尾巴,以便吃到食物后增长,按食物大小增长长度
}
var food={
color:'#8F401F',
size:10,
position:{x:200,y:200},//食物出现的位置
}
var map={
color:'#868686',
}
var Game=function(){
this.score=0;//计分
this.isDealControl=true;//是否处理了方向控制
this.pause=false;//游戏暂停
this.direction=direction;//方向枚举
this.snake=snake;//蛇
this.food=food;//食物
this.map=map; //地图
this.canvas=document.getElementById('canvas');
this.canvas.width = 300;
this.canvas.height = 300;
this.cxt=this.canvas.getContext("2d");
let speed=localStorage.getItem("snake_speed");
if(speed)
this.snake.speed=parseInt(speed);
else
localStorage.setItem('snake_speed',this.snake.speed);
this.init();
this.draw_map();
this.create_food();
window.onkeyup=this.control.bind(this);
}
Game.prototype.draw_map=function(){
this.cxt.fillStyle=this.map.color;
this.cxt.fillRect(0,0,this.canvas.width,this.canvas.height);
}
Game.prototype.reset_map=function(){
this.cxt.clearRect(0,0,this.canvas.width,this.canvas.height);
this.draw_map();
this.draw_snake();
this.draw_food();
}
Game.prototype.draw_snake=function(){
for(var i in this.snake.body){
if(i==0)//头部
this.cxt.fillStyle=this.snake.headColor;
else
this.cxt.fillStyle=this.snake.color;
this.cxt.fillRect(this.snake.body[i].x-this.snake.size/2,this.snake.body[i].y-this.snake.size/2,this.snake.size,this.snake.size);
}
}
Game.prototype.create_food=function(){
this.food.size=10+Math.round(Math.random()*20);
this.food.position.x=Math.round(Math.random()*this.canvas.width);
this.food.position.y=Math.round(Math.random()*this.canvas.height);
if(this.food.position.x<this.food.size/2)this.food.position.x=this.food.size/2;
if(this.food.position.y<this.food.size/2)this.food.position.y=this.food.size/2;
if(this.food.position.x>this.canvas.width-this.food.size/2)this.food.position.x=this.canvas.width-this.food.size/2
if(this.food.position.y>this.canvas.height-this.food.size/2)this.food.position.y=this.canvas.height-this.food.size/2
}
Game.prototype.draw_food=function(){
this.cxt.fillStyle=this.food.color;
this.cxt.fillRect(this.food.position.x-this.food.size/2,this.food.position.y-this.food.size/2,this.food.size,this.food.size);
}
Game.prototype.move=function(){
const json=JSON.stringify(this.snake.body);
const oldbody=JSON.parse(json);
this.snake.tail.push(oldbody[oldbody.length-1]);
//续上尾巴个数
let tailCount=1;
//每截身体移动:头部移动,之后的每一节=前一节
for(var i=0;i<this.snake.length;i++){
if(i==0)
{
switch(this.snake.direction){
case direction.UP:this.snake.body[i].y-=this.snake.size;break;
case direction.RIGHT:this.snake.body[i].x+=this.snake.size;break;
case direction.DOWN:this.snake.body[i].y+=this.snake.size;break;
case direction.LEFT:this.snake.body[i].x-=this.snake.size;break;
}
}
else{
if(i<this.snake.body.length){
this.snake.body[i]=oldbody[i-1]
}else{
this.snake.body[i]=this.snake.tail[this.snake.tail.length-tailCount]
tailCount++;
}
}
}
this.isDealControl=true;
//碰撞检测
//碰撞墙壁
if(this.snake.body[0].x-this.snake.size/2<0||this.snake.body[0].x+this.snake.size/2>this.canvas.width||this.snake.body[0].y-this.snake.size/2<0||this.snake.body[0].y+this.snake.size/2>this.canvas.height){
return false;
}
//碰撞自己 xy均有重叠
for(var i=1;i<this.snake.body.length;i++) {
if(
Math.abs(this.snake.body[0].x - this.snake.body[i].x) < this.snake.size/2 + this.snake.size/2
&&
Math.abs(this.snake.body[0].y - this.snake.body[i].y) < this.snake.size/2 + this.snake.size/2
){
return false;
}
}
//碰撞食物
if(
Math.abs(this.snake.body[0].x - this.food.position.x) < this.snake.size/2 + this.food.size/2
&&
Math.abs(this.snake.body[0].y - this.food.position.y) < this.snake.size/2 + this.food.size/2
){
this.create_food();
this.snake.length+=Math.ceil(this.food.size/this.snake.size);
this.score+=this.food.size;
}
return true;
}
Game.prototype.control=function(e){
if(!this.isDealControl)
return;
switch(e.keyCode){
case 37:if(this.snake.direction!==direction.RIGHT){this.snake.direction=direction.LEFT;this.isDealControl=false;}break;
case 38:if(this.snake.direction!==direction.DOWN){this.snake.direction=direction.UP;this.isDealControl=false;}break;
case 39:if(this.snake.direction!==direction.LEFT){this.snake.direction=direction.RIGHT;this.isDealControl=false;}break;
case 40:if(this.snake.direction!==direction.UP){this.snake.direction=direction.DOWN;this.isDealControl=false;}break;
case 32:
this.pause=!this.pause;
if(!this.pause)this.go();
break;
}
}
Game.prototype.init=function(){
let that=this;
let slider_bar=document.getElementsByClassName('slider-bar')[0];
let slider_block=document.getElementsByClassName('slider-block')[0];
let moveX;
slider_block.style.left = (slider_bar.offsetLeft+(200-this.snake.speed)*2)+'px';
slider_block.onmousedown=function(event){
var event = event || window.event;
var diffX = event.clientX - slider_block.offsetLeft;
var diffY = event.clientY - slider_block.offsetTop;
if(typeof slider_block.setCapture !== 'undefined'){
slider_block.setCapture();
}
document.onmousemove = function(event){
var event = event || window.event;
moveX = event.clientX - diffX;
if(moveX<slider_bar.offsetLeft)
moveX=slider_bar.offsetLeft;
if(moveX>slider_bar.offsetLeft+slider_bar.offsetWidth)
moveX=slider_bar.offsetLeft+slider_bar.offsetWidth
// var moveY = event.clientY - diffY;
if(moveX < 0){
moveX = 0
}else if(moveX > window.innerWidth - slider_block.offsetWidth){
moveX = window.innerWidth - slider_block.offsetWidth
}
// if(moveY < 0){
// moveY = 0
// }else if(moveY > window.innerHeight - slider_block.offsetHeight){
// moveY = window.innerHeight - slider_block.offsetHeight
// }
slider_block.style.left = moveX + 'px';
// slider_block.style.top = moveY + 'px'
}
document.onmouseup = function(event){
this.onmousemove = null;
this.onmouseup = null;
//value:0-300 speed:200-50
let value=moveX-slider_bar.offsetLeft;
let speed=200-value*0.5;
localStorage.setItem('snake_speed',speed);
that.snake.speed=speed;
//低版本ie bug
if(typeof slider_block.releaseCapture!='undefined'){
slider_block.releaseCapture();
}
}
}
}
Game.prototype.go=function(){
if(!this.move())
{
alert('你没了');
return
}
this.reset_map();
//计分
document.getElementById('score').innerHTML=this.score;
if(this.pause)return;
setTimeout(() => {
this.go();
}, this.snake.speed);
}
var game=new Game();
game.go();
</script>
</html>