赞
踩
配置如下
{
"compilerOptions": {
"target": "es2015",
"module": "es2015",
"strict": true,
"outDir": "./dist",
"noEmitOnError": true
}
}
安装 webpack
npm i webpack webpack-cli -D
配置
配置 webpack 项目工程化,配置项目运行打包,兼容,处理 .ts .css .less html 文件
安装插件
npm i html-webpack-plugin webpack-dev-server -D
webpack-dev-server 作用
webpack 内部服务器,可以在开发阶段项目自动运行,比如修改了代码,会检测到改动并且自动运行,不需要每次都手动运行查看,利于开发效率
html-webpack-plugin
html 插件,可用来提供 html 模板,打包后的 dist 文件中的 html 会根据这个模板生成
配置如下
const path = require('path') // html 插件 const HtmlWebpackPlugin = require('html-webpack-plugin') const htmlPlugin = new HtmlWebpackPlugin({ template: './src/index.html', filename: './index.html' }) module.exports = { mode: 'development', entry: './src/index.ts', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', environment: { // 兼容 IE ,禁止使用箭头函数 arrowFunction: false, // 兼容 IE ,禁止使用es6语法 const: false } }, module: { rules: [{ test: /\.ts$/, use: [ // 配置babel { // 指定加载器 loader: 'babel-loader', // 设置 babel options: { // 设置预定义环境 presets: [ [ // 指定环境插件 "@babel/preset-env", // 配置信息 { // 要兼容的目标浏览器 targets: { "chrome": "58" }, // 指定 corejs 版本 "corejs": "3", // 使用 corejs 的方式"usage"表示按需加载 "useBuiltIns": "usage" } ] ] } }, 'ts-loader' ], exclude: /node_modules/ }, { test: /\.less$/, use: [ "style-loader", "css-loader", // 引入 postcss { loader: "postcss-loader", options: { postcssOptions: { plugins: [ [ "postcss-preset-env", { browsers: 'last 2 versions' } ] ] } } }, "less-loader" ] } ] }, plugins: [htmlPlugin], resolve: { extensions: ['.ts', '.js'] } }
在项目名称是英文的情况下使用
npm init -y
如果项目名称不是英文,使用如下命令,给它设置一个英文名称
npm init
安装项目所需的全部开发依赖,如下
配置就说这么多,下面开始项目代码
采用的是结构与数据分离
先布局基本样式
在项目根目录下创建 src 文件夹,在src 文件夹下创建 index.html 和 index.ts 文件
./src/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>贪吃蛇</title> </head> <body> <div class="box"> <div class="top"> // 蛇 <div id="snake"> // 蛇的每一节 <div></div> </div> // 食物 <div id="food"></div> </div> // 记分牌 <div class="bottom"> <div>SCORE:<span id="score">3</span></div> <div>LEVEL:<span id="level">1</span></div> </div> </div> </body> </html>
在 src 目录下创建 style 文件夹存放样式文件 index.less
* { margin: 0; padding: 0; box-sizing: border-box; } body { font: bold 20px "Courier"; } .box { width: 360px; height: 420px; background-color: #b7d4a8; border: 10px solid #000; margin: 100px auto; border-radius: 20px; display: flex; flex-direction: column; align-items: center; justify-content: space-evenly; } .top { width: 304px; height: 304px; border: 2px solid #000; position: relative; } .bottom { width: 304px; display: flex; justify-content: space-between; } #snake { position: relative; } #snake div { width: 10px; height: 10px; background-color: black; border: 1px solid #b7d4a8; position: absolute; top: 0; left: 0; } #food { width: 10px; height: 10px; background-color: black; position: absolute; border: 1px solid #b7d4a8; }
在 index.ts 文件中引入样式
import './style/index.less'
开始业务逻辑代码
在 src 文件下创建 modules 文件夹,存放每个类
Food.ts , 控制食物随机出现的位置
class Food { // 定义一个属性表示事物所对应的元素 element: HTMLElement constructor() { // !感叹号的意思是不用管 this.element = document.getElementById('food')! } // 定义一个获取食物X轴坐标的方法 get X() { return this.element.offsetLeft } // 定义一个获取食物Y轴坐标的方法 get Y() { return this.element.offsetTop } // 修改十五位置的方法 change() { // 生成一个随机数 // 食物的位置最小时0,最大是290 // Math.round(Math.random() * 29) * 10 this.element.style.top = Math.round(Math.random() * 29) * 10 + 'px' this.element.style.left = Math.round(Math.random() * 29) * 10 + 'px' } } // let food = new Food() // food.change() // console.log(food.X, food.Y) export default Food
创建 ScorePanel.ts,控制记分牌的变化
// 定义记分牌 class ScorePanel { // score, level 记录分数和等级 score = 0 level = 1 // 分数和等级初始化 scoreEle: HTMLElement levelEle: HTMLElement // shezhibianliang maxLevel: number maxScore: number constructor(maxLevel: number = 10, maxScore: number = 10) { this.scoreEle = document.getElementById('score')! this.levelEle = document.getElementById('level')! this.maxLevel = maxLevel this.maxScore = maxScore } // 设置加分的方法 addScore() { this.score++ this.scoreEle.innerHTML = this.score + '' if (this.score % this.maxScore === 0) { this.levelUp() } } // 提升等级的方法 levelUp() { if (this.level < this.maxLevel) { this.level++ this.levelEle.innerHTML = this.level + '' } } } export default ScorePanel
创建 Snake.ts,控制蛇的长度/位置/是否撞墙或者撞到自己
class Snake { // 表示蛇头的元素 head: HTMLElement // 蛇的身体 bodies: HTMLCollection element: HTMLElement constructor() { this.element = document.getElementById('snake')! this.head = document.querySelector('#snake > div')! this.bodies = this.element.getElementsByTagName('div') } // 获取蛇的坐标 get X() { return this.head.offsetLeft } get Y() { return this.head.offsetTop } // 设置蛇头的坐标 set X(value: number) { if (this.X === value) return // 判断是否撞墙 X值的合法范围 if (value < 0 || value > 290) { // 蛇撞墙了抛出异常 throw new Error("蛇撞墙了"); } // 修改水平座标蛇向右走不能往左走 if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) { // 如果发生了掉头让蛇继续移动 if (value > this.X) { value = this.X -10 } else { value = this.X + 10 } } // 移动身体 this.moveBody() this.head.style.left = value + 'px' // 检查有没有撞到自己 this.checkHeadBody() } set Y(value: number) { if (this.Y === value) return if (value < 0 || value > 290) { throw new Error("蛇撞墙了"); } // 修改水平座标蛇向右走不能往左走 if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) { // 如果发生了掉头让蛇继续移动 if (value > this.Y) { value = this.Y -10 } else { value = this.Y + 10 } } // 移动身体 this.moveBody() this.head.style.top = value + 'px' // 检查有没有撞到自己 this.checkHeadBody() } addBody() { this.element.insertAdjacentHTML('beforeend', '<div></div>') } moveBody() { // 从后往前改,将后面的身体设置位前面的身体位置 // 遍历获取所有的身体 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'; } } checkHeadBody() { for (let i = 1; i < this.bodies.length; i++){ if (this.X === (this.bodies[i] as HTMLElement).offsetLeft && this.Y === (this.bodies[i] as HTMLElement).offsetTop) { throw new Error("撞到自己了"); } } } } export default Snake
创建 GameControl.ts,控制蛇的移动,把蛇、食物、记分牌联系到一块
import Snake from "./snake"; import Food from "./food"; import ScorePanel from "./ScorePanel"; class GameControl { // 定义三个属性 // 蛇 snake: Snake food: Food scorePanel: ScorePanel // c创建一个属性来存储蛇的移动方向 direction: string = 'Right' // 创建一个属性用来记录游戏是否结束 isLive: boolean = true constructor() { this.snake = new Snake() this.food = new Food() this.scorePanel = new ScorePanel() this.init() } // 游戏初始化方法 init() { // 绑定键盘事件 // 修改this指向 document.addEventListener('keydown', this.keydownHandler.bind(this)) // 调用 run 方法 this.run() } // 创建键盘按下的函数 keydownHandler(e: KeyboardEvent) { // 修改 dirention 的值 this.direction = e.key } // 创建控制蛇移动的方法 run() { // 根据方向来改变蛇的位置 // 获取蛇现在的坐标 let X = this.snake.X let Y = this.snake.Y switch (this.direction) { case "ArrowUp": case "Up": Y -= 10 break; case "ArrowDown": case "Down": Y += 10 break; case "ArrowRight": case "Right": X += 10 break; case "ArrowLeft": case "Left": X -= 10 break; default: break; } this.checkEat(X, Y) try { this.snake.X = X this.snake.Y = Y } catch (e) { alert(' OVER!') this.isLive = false } this.isLive && setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30); } // 定义一个方法判断吃到食物 checkEat(X: Number, Y: number) { if (X === this.food.X && Y === this.food.Y) { this.food.change() this.scorePanel.addScore() this.snake.addBody() } } } export default GameControl
在 src 下的 index.ts 中引入 GameControl 并且实例化,就可以运行代码
import GameControl from './modules/GameControl'
new GameControl()
gitee 项目地址:
https://gitee.com/jinyang465/snake-demo
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。