1. 项目搭建配置
// webpack.config.js tsconfig.json package.json配置代码
// 1. package.json
{
"name": "part5",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode development",
"start": "webpack serve --open --mode development"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.18.13",
"@babel/preset-env": "^7.18.10",
"babel-loader": "^8.2.5",
"clean-webpack-plugin": "^4.0.0",
"core-js": "^3.25.0",
"html-webpack-plugin": "^5.5.0",
"ts-loader": "^9.3.1",
"typescript": "^4.8.2",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.10.1"
}
}
// 2. tsconfig.json
{
"compilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true,
"noEmitOnError": true
}
}
// 3 .webpack.config.js
// 引入一个包
const path = require('path');
// 引入html插件
const HTMLWebpackPlugin = require('html-webpack-plugin');
// 引入clean插件
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
// 指定入口文件
entry: "./src/index.ts",
// 指定打包文件所在目录
output: {
// 指定打包的目录
path: path.resolve(__dirname, 'dist'),
// 打包后的文件
filename: "bundle.js",
// 告诉webpack不使用箭头函数
environment: {
arrowFunction: false
}
},
// 指定webpack打包时要使用模块
module: {
// 指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
// 要使用的loader
use: [
// 配置babel
{
// 指定加载器
loader: "babel-loader",
// 设置babel
options: {
// 设置预定义的环境
presets: [
[
// 指定环境的插件
"@babel/preset-env",
// 配置信息
{
// 要兼容的目标浏览器
targets: {
"chrome": "88"
},
// 指定corejs版本
"corejs": "3",
// 使用codejs的方式:usage表示按需加载
"useBuiltIns": "usage"
}
]
]
}
},
'ts-loader' // 后面的先执行
],
// 要排除的文件
exclude: /node_modules/
},
// 设置less文件的处理
{
test: /\.less$/,
use: [ // 从下往上执行,先less 后css 最后style
"style-loader",
// 引入postcss
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
// 兼容最新的2个浏览器
browsers: 'last 2 versions'
}
]
]
}
}
},
"css-loader",
"less-loader"
]
}
]
},
// 配置webpack插件
plugins: [
new CleanWebpackPlugin(),
new HTMLWebpackPlugin({
// title: "这是一个自定义的title"
template: "./src/index.html"
})
],
// 用来设置引用模块, 告诉它凡是以js,ts结尾都是可以使用模块化
resolve: {
extensions: ['.ts', '.js']
}
};
2. 创建part5文件夹
// 创建part文件夹, 将上方三个配置放置part文件夹下方,然后使用npm i安装依赖
// 在part文件夹下创建src,src下创建index.html和index.ts, 然后使用npm run build打包
// 安装less,npm i -D less less-loader css-loader style-loader
// webpack配置完less后,直接npm start运行项目
// 在src下创建style文件夹,style下创建index.less
body {
background: 'red';
}
// 在index.ts 引入样式, 然后就可以检测页面是否生效
import './style/index.less';
// 继续安装依赖, 然后在webpack配置
npm i -D postcss postcss-loader postcss-preset-env
3. 贪吃蛇ts代码(本例子只填写关于ts,html和css在这不做笔记)
// 定义类
class Food {
// 定义一个属性表示食物所对应的元素
element: HTMLElement;
constructor () {
// 后面不加!会报错,因为document.getElementById('food')可能为空,后面加!表示一定不会为空
this.element = document.getElementById('food')!;
}
// 定义一个获取食物X轴坐标的方法
get X () {
return this.element.offsetLeft;
}
get Y () {
return this.element.offsetTop;
}
// 修改食物的位置
change () {
// 生成一个随机的位置
// 食物的位置最小是0最大是290
// 蛇移动一次就是一格,一格的大小就是10,所以食物的坐标必须是整10
// Math.random(Math.random() * 29); 0-29
const top = Math.round(Math.random() * 29) * 10;
const left = Math.round(Math.random() * 29) * 10;
this.element.style.left = left + 'px';
this.element.style.top = top + 'px';
}
}
// 测试代码
// const food = new Food();
// console.log(food.X, food.Y); // 40 100
// food.change();
// console.log(food.X, food.Y); // 80 180
// 定义表示记分牌的类
class ScorePanel {
// score和level用来记录分数和等级
score = 0;
level = 1;
// 分数和等级所在的元素, 在构造函数中进行初始化
scoreEle: HTMLElement;
levelEle: HTMLElement;
// 设置一格变量限制等级
maxLevel: number;
// 设置一个变量表示多少分时升级
upScore: number
constructor (maxLevel: number = 10, upScore: number = 10) {
this.scoreEle = document.getElementById('score')!;
this.levelEle = document.getElementById('level')!;
this.maxLevel = maxLevel;
this.upScore = upScore;
}
// 设置一个加分的方法
addScore () {
// 分数自增
this.score++;
this.scoreEle.innerHTML = this.score + '';
if (this.score % this.upScore === 0) {
this.levelUp();
}
}
// 提升等级的方法
levelUp() {
if (this.level < this.maxLevel) {
this.levelEle.innerHTML = ++this.level + '';
}
}
}
// 测试
const scorePanel = new ScorePanel();
scorePanel.addScore();
// 蛇的类
class Snack {
// 表示蛇头的元素
head: HTMLElement;
// 蛇的身体(包括蛇头)
bodies: HTMLCollection;
// 获取蛇的容器
element: HTMLElement;
constructor () {
this.element = document.getElementById('snack')!;
this.head = document.querySelector('#snack > div')! as HTMLElement;
this.bodies = document.getElementById('snack')!.getElementsByTagName('div');
}
// 获取蛇的坐标(蛇头)
get X () {
return this.head.offsetLeft;
}
// 获取蛇的Y轴坐标
get Y() {
return this.head.offsetTop;
}
set X (value: number) {
// 如果新值和旧值相同,则直接返回,不再修改
if (this.X === value) {
return;
}
// X的值的合法范围0-290之间
if (value < 0 || value > 290) {
// 进入判断,说明蛇撞墙了, 抛出一个异常
throw new Error('蛇撞墙了');
}
this.head.style.left = value + 'px';
}
set Y (value: number) {
// 如果新值和旧值相同,则直接返回,不再修改
if (this.Y === value) {
return;
}
// Y的值的合法范围0-290之间
if (value < 0 || value > 290) {
// 进入判断,说明蛇撞墙了, 抛出一个异常
throw new Error('蛇撞墙了');
}
this.head.style.top = value + 'px';
}
// 蛇增加身体的方法
addBody() {
// 向element中添加一个div
this.element.insertAdjacentHTML('afterend', "<div></div>");
}
}
// 游戏控制器, 控制其他的所有类
class gameControl {
// 定义三个属性
// 蛇
snack: Snack;
// 食物
food: Food;
// 记分牌
scorePanel: ScorePanel;
// 创建一个属性来存储蛇的移动方向
direction: string = '';
// 创建一个属性用来记录游戏是否结束
isLive = true;
constructor () {
this.snack = new Snack();
this.food = new Food;
this.scorePanel = new ScorePanel;
this.init();
}
// 游戏初始化方法, 调用后游戏即开始
init () {
// 绑定键盘按键按下的事件
document.addEventListener('keydown', this.keyDownHandler.bind(this));
// 调用init方法
this.run();
}
// 创建一个键盘按下的响应函数
keyDownHandler = (event: KeyboardEvent) => {
// chrome IE
// ArrowUp up
// ArrowDown down
// ArrowLeft left
// ArrowRight right
// 在IE和Chrome值不一样
// console.log(event.key);
// 需要检查值是否合法(用户是否按了正确的按键)
this.direction = event.key;
}
// 创建一个控制蛇移动的方法
run () {
// 根据方向(this.direction)来使蛇的位置改变
/**
* 向上 top减少
* 向下 top增加
* 向左 left减少
* 向右 left增加
*/
// 获取蛇现在的坐标
let X = this.snack.X;
let Y = this.snack.Y;
// 根据按键方向来修改X值和Y值
switch (this.direction) {
case 'ArrowUp':
case 'up':
// 向上移动 top减少
Y -= 10;
break;
case 'ArrowDown':
case 'down':
// 向下移动 top增加
Y += 10;
break;
case 'ArrowLeft':
case 'left':
// 向左移动 left减少
X -= 10;
break;
case 'ArrowRight':
case 'right':
X += 10;
// 向右移动 left增加
break;
}
// 检查蛇是否迟到了食物
this.checkEat(X, Y);
// 修改蛇的X和Y值
try {
// 修改蛇的X和Y值
this.snack.X = X;
this.snack.Y = Y;
} catch (error) {
// 进入catch 说明出现了异常,游戏结束,弹出一个提示信息
alert((error as Error).message);
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.snack.addBody();
}
}
}