参考资料
开发工具
vscode 推荐插件 Live Server 可以监控文件的变化,并进行刷新
界面效果
界面index.html
<!DOCTYPE html>
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< meta name = " viewport" content = " width=device-width, initial-scale=1.0" >
< title> 贪吃蛇</ title>
< link rel = " stylesheet" href = " css/index.css" >
</ head>
< body>
< div id = " main" >
< div id = " stage" >
< div id = " snake" >
< div> </ div>
</ div>
< div id = " food" >
< div> </ div>
< div> </ div>
< div> </ div>
< div> </ div>
</ div>
</ div>
< div id = " score-panel" >
< div> SCORE:< span id = " score" > 0</ span> </ div>
< div> LEVEL:< span id = " level" > 1</ span> </ div>
</ div>
</ div>
< script src = " TS/index.js" > </ script>
</ body>
</ html>
index.css
* {
margin : 0;
padding : 0;
box-sizing : border-box;
}
body {
font : bold 20px "Courier"
}
#main {
width : 360px;
height : 420px;
background-color : #b7d4a8;
margin : 100px auto;
border : 10px solid black;
border-radius : 40px;
display : flex;
flex-flow : column;
align-items : center;
justify-content : space-around;
}
#stage {
width : 304px;
height : 304px;
border : 2px solid black;
position : relative;
}
#snake>div {
width : 10px;
height : 10px;
background-color : black;
border : 1px solid #b7d4a8;
position : absolute;
}
#food {
width : 10px;
height : 10px;
position : absolute;
left : 40px;
top : 100px;
display : flex;
flex-flow : row wrap;
justify-content : space-between;
align-items : center;
}
#food > div {
width : 4px;
height : 4px;
background-color : black;
transform : rotate ( 45deg) ;
}
#score-panel {
width : 300px;
display : flex;
justify-content : space-between;
}
index.ts
在模块化开发中可以把每个类单独放在一个文件中 在这里我使用一个文件,可以使用 命令进行便于 tsc -t ES6 -w index.ts
表示使用ES6进行编译,并且对文件进行监控
class Food {
element : HTMLElement
width: number
size: number
constructor ( id: string , width = 29 , size = 10 ) {
this . element= document. getElementById ( id)
this . width = width
this . size = size
}
get X ( ) {
return this . element. offsetLeft
}
get Y ( ) {
return this . element. offsetTop
}
change ( ) {
this . element. style. left = Math. round ( Math. random ( ) * this . width) * this . size + 'px'
this . element. style. top = Math. round ( Math. random ( ) * this . width) * this . size + 'px'
}
}
class ScorePanel {
score = 0
level = 1
scoreEle : HTMLElement
levelEle : HTMLElement
static maxLevel = 10
static upScore = 1
constructor ( scoreId: string , levelId: string ) {
this . scoreEle = document. getElementById ( scoreId)
this . levelEle = document. getElementById ( levelId)
this . scoreEle. innerText = this . score + ''
this . levelEle. innerText = this . level + ''
}
addScore ( d = 1 ) {
this . score += 1
this . scoreEle. innerText = this . score + ''
if ( this . score % ScorePanel. upScore === 0 ) this . addLevel ( )
}
addLevel ( ) {
if ( this . level < ScorePanel. maxLevel) {
this . levelEle. innerText = ++ this . level + ''
}
}
}
class Snake {
head: HTMLElement
bodies: HTMLCollection
element: HTMLElement
range: number
constructor ( id, range = 290 ) {
this . element = document. getElementById ( id)
this . element. innerHTML = '<div></div>'
this . bodies = this . element. getElementsByTagName ( 'div' )
this . head = this . bodies[ 0 ] as HTMLElement
this . range = range
}
get X ( ) {
return this . head. offsetLeft
}
get Y ( ) {
return this . head. offsetTop
}
set X ( value: number ) {
if ( this . X === value) return
this . judge ( value)
this . move ( )
this . head. style. left = value + 'px'
}
set Y ( value: number ) {
if ( this . Y === value) return
this . judge ( value)
this . move ( )
this . head. style. top = value + 'px'
}
addBody ( ) {
this . element. insertAdjacentHTML ( "beforeend" , "<div></div>" )
}
move ( ) {
for ( let i= this . bodies. length- 1 ; i> 0 ; -- i) {
let x = ( this . bodies[ i- 1 ] as HTMLElement) . offsetLeft
let y = ( this . bodies[ i- 1 ] as HTMLElement) . offsetTop;
( this . bodies[ i] as HTMLElement) . style. left = x + 'px' ;
( this . bodies[ i] as HTMLElement) . style. top = y + 'px'
}
}
judge ( value: number ) {
if ( value < 0 || value > this . range) {
throw new Error ( '蛇撞墙了' )
}
for ( let i= this . bodies. length- 1 ; i> 0 ; -- i) {
let x = ( this . bodies[ i] as HTMLElement) . offsetLeft
let y = ( this . bodies[ i] as HTMLElement) . offsetTop;
if ( x=== this . X && y === this . Y ) throw new Error ( '不能吃自己' )
}
}
}
class GameControl {
snake: Snake
scorePanel: ScorePanel
food: Food
direction: string
isLive = true
static directionKey = new Set ( [ 'w' , 'a' , 's' , 'd' , ' ' ] )
static speed = 200
constructor ( ) {
this . scorePanel = new ScorePanel ( 'score' , 'level' )
this . food = new Food ( "food" )
this . snake = new Snake ( "snake" , this . food. size * this . food. width)
this . direction = ' '
this . init ( )
}
init ( ) {
this . food. change ( )
console . log ( this . food. X , this . food. Y )
document. addEventListener ( 'keydown' , this . keydownHandler. bind ( this ) )
this . run ( )
}
keydownHandler ( event: KeyboardEvent) {
let key = event. key
if ( GameControl. directionKey. has ( key) ) {
if ( this . direction === 'w' && key === 's' ) return
if ( this . direction === 'a' && key === 'd' ) return
if ( this . direction === 's' && key === 'w' ) return
if ( this . direction === 'd' && key === 'a' ) return
this . direction = event. key
}
}
run ( ) {
let x = this . snake. X , y = this . snake. Y
switch ( this . direction) {
case 'w' :
y -= 10
break
case 'a' :
x -= 10
break
case 's' :
y += 10
break
case 'd' :
x += 10
break
case ' ' :
break
}
try {
this . checkEat ( x, y)
this . snake. X = x
this . snake. Y = y
} catch ( err ) {
this . isLive = false
if ( window. confirm ( err. message + ' 是否继续游戏' ) ) {
game = new GameControl ( )
}
return
}
this . isLive && setTimeout ( this . run. bind ( this ) , GameControl. speed / this . scorePanel. level)
}
checkEat ( x: number , y: number ) {
if ( x === this . food. X && y === this . food. Y ) {
this . food. change ( )
this . scorePanel. addScore ( )
this . snake. addBody ( )
}
}
}
let game: GameControl = new GameControl ( )
附录
老师的webpack.config.js 配置文件,其他一部分是我添加
const path = require ( 'path' )
const HTMLWebpackPlugin = require ( 'html-webpack-plugin' )
const { CleanWebpackPlugin} = require ( 'clean-webpack-plugin' )
module. exports = {
context: path. resolve ( __dirname, '' ) ,
entry: "./src/index.ts" ,
output: {
path: path. resolve ( __dirname, 'dist' ) ,
filename: "bundle.js" ,
environment: {
arrowFunction: false
}
} ,
module: {
rules: [
{
test: /\.tsx?$/ ,
use: [
{
loader: "babel-loader" ,
options: {
persets: [
"@babel/preset-env" ,
{
targets: {
"chrome" : "88" ,
"ie" : "10"
} ,
"corejs" : "3" ,
"useBuiltIns" : "usage" ,
}
]
}
} ,
"ts-loader"
] ,
exclude: "/node-modules/"
}
]
} ,
resolve: {
extensions: [ "" , ".webpack.js" , ".web.js" , ".ts" , ".tsx" , ".js" ] ,
alias: {
'@' : resolve ( 'src' )
} ,
} ,
plugins: [
new CleanWebpackPlugin ( ) ,
new HTMLWebpackPlugin ( {
title : 'ts项目构建' ,
template: "" ,
} )
] ,
devServer: {
}
}
{
"script" : {
"build" : "webpack" ,
"start" : "webpack serve --open chrome.exe"
} ,
"devDependencies" : {
"clean-webpack-plugin" : "" ,
"html-webpack-plugin" : "" ,
"ts-loader" : "" ,
"typescript" : "" ,
"webpack" : "" ,
"webpack-cli" : "" ,
"webpack-dev-server" : "" ,
"@babel/core" : "" ,
"@babel/preset-enb" : "" ,
"babel-loader" : "" ,
"core-js" : ""
}
}
webpack-dev-server
: 拥有实时重载能力的静态资源服务器