尚硅谷TypeScript教程(李立超老师TS新课)
1. 创建开发环境
- 创建工程,使用学习笔记的第二部分
- 安装css部分
npm i -D less less-loader css-loader style-loader
- 对css部分处理,能够运行在低版本浏览器
npm i -D postcss postcss-loader postcss-preset-env
- 修改webpack.config.json文件
// 设置less文件的处理
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions:{
plugins: [
[
"postcss-preset-env",
{
browsers:"last 2 versions"
}
]
]
}
}
},
"less-loader"
]
}
2. 工程目录结构
3. 代码部分
- index.js
import './style/index.less'
import GameControl from "./modules/GameControl";
new GameControl()
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>贪吃蛇</title>
</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>
</body>
</html>
- /style/index.less
// 设置变量
@bg-color:#b7d4a8;
*{
margin: 0;
padding: 0;
caret-color: transparent;
// 改变盒子模型的计算方式
box-sizing: border-box;
}
body{
font: bold 20px "Courier";
}
#main{
width: 360px;
height: 420px;
background-color: @bg-color;
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: #000;
border:1px solid @bg-color;
// 开启绝对定位
position: absolute;
}
}
#food{
width: 10px;
height: 10px;
border:1px solid @bg-color;
// 开启绝对定位
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: space-between;
justify-content: space-between;
&>div{
width: 4px;
height: 4px;
background-color: black;
transform: rotate(45deg);
}
}
}
#score-panel{
width: 300px;
display: flex;
justify-content: space-between;
}
}
- /modules/snake.ts
export default class Snake{
// 蛇头
head:HTMLElement
// 蛇身体,包括蛇头
bodies:HTMLCollection
// 蛇的父容器
element:HTMLElement
constructor() {
this.element = document.getElementById("snake")!
this.bodies = this.element.children
this.head = this.element.firstElementChild as HTMLElement
}
// 蛇头的坐标
get X(){
return this.head.offsetLeft
}
get Y(){
return this.head.offsetTop
}
// 设置蛇头的坐标
set X(value){
// 当蛇的第二节与蛇头的x轴坐标重合,说明发生了水平方向的掉头
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'
}
set Y(value){
// 当蛇的第二节与蛇头的Y轴坐标重合,发生了垂直方向的掉头
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'
}
// 蛇增加身体
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';
}
}
checkHandBody():boolean{
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){
return false
}
}
return true
}
}
- /modules/ScorePanel.ts
export default class ScorePanel{
score = 0
level = 1
scoreElement : HTMLElement
levelElement : HTMLElement
maxLevel : number
upScore : number
constructor(maxLevel = 10,upScore = 10) {
this.scoreElement = document.getElementById('score')!
this.levelElement = document.getElementById('level')!
this.maxLevel = maxLevel // 最大等级
this.upScore = upScore // 多少分升一级
}
// 增加积分,每吃一个食物增加1分,在此处设置等级的增加
addScore(){
this.score ++
this.scoreElement.innerText = this.score.toString()
if (this.score % this.upScore === 0){
this.addLevel()
}
}
// 增加等级
addLevel(){
if ( ++this.level <= this.maxLevel) {
this.levelElement.innerText = this.level.toString()
}
}
}
// const a = new ScorePanel()
// for (let i = 0; i < 200; i++) {
// a.addScore()
// }
- /modules/Gamecontrol.ts
import Snake from "./snake";
import Food from "./Food";
import ScorePanel from "./ScorePanel";
export default class GameControl {
snake: Snake
food: Food
scorePanel: ScorePanel
dircter = '' // 保存按键的值
constructor() {
this.snake = new Snake()
this.food = new Food()
this.scorePanel = new ScorePanel()
this.init()
}
// 初始化操作,监听按键事件
init() {
// 第二个参数如果不写bind(this),那调用的函数中的this就是document,而不是类的实例化对象
document.addEventListener("keydown", this.keydownHandler.bind(this))
this.run()
}
// 按键的回调函数
keydownHandler(event: KeyboardEvent) {
this.dircter = event.key
}
run() {
// 蛇头的原坐标
let x = this.snake.X
let y = this.snake.Y
// 按下按键后,4个方向中某一个方向的坐标需要修改
switch (this.dircter) {
case 'ArrowUp':
case 'Up':
y = y - 10
break;
case 'ArrowDown':
case 'Down':
y = y + 10
break
case 'ArrowLeft':
case 'Left':
x = x - 10
break
case 'ArrowRight':
case 'Right':
x = x + 10
break
}
// 蛇头下一步是不是撞墙了
if (x < 0 || x > 290 || y < 0 || y > 290) {
alert("蛇撞墙了")
return
// 检测蛇头是否撞到自己
} else if (!this.snake.checkHandBody()) {
alert('你咬到自己尾巴了')
return
} else {
this.eachFood()
// 同一时间只可能有一个轴的位置发生改变
if (this.snake.Y === y) {
this.snake.X = x
}
else if (this.snake.X === x) {
this.snake.Y = y
}
// 开启定时器,随着等级的升高,蛇的移动速度越来越快
let time = this.scorePanel.level * 50
setTimeout(this.run.bind(this), 250 - time)
}
}
// 检测蛇是否吃到食物,吃到食物后:新增食物,增加积分,增加蛇的身体
eachFood() {
if (this.snake.X === this.food.X && this.snake.Y === this.food.Y) {
this.food.change()
this.scorePanel.addScore()
this.snake.addBody()
}
}
}
- /modules/Food.ts
class Food{
element:HTMLElement
constructor() {
this.element = document.getElementById('food')!
this.change()
}
// 获取食物坐标
get X(){
return this.element.offsetLeft
}
get Y(){
return this.element.offsetTop
}
// 设置食物坐标
set X(value ){
this.element.style.left = value + 'px'
}
set Y(value){
this.element.style.top = value + 'px'
}
// 随机在地图上新增食物
change(){
let top = Math.round(Math.random()*29) *10
let left = Math.round(Math.random()*29) *10
this.X = left
this.Y = top
}
}
export default Food
// const a = new Food()
// console.log(a.X,a.Y);
// a.change()
// console.log(a.X,a.Y);
4. 配置文件
- package.json
{
"name": "part2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack serve --open"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.23.3",
"@babel/preset-env": "^7.23.3",
"babel-loader": "^9.1.3",
"clean-webpack-plugin": "^4.0.0",
"core-js": "^3.33.3",
"css-loader": "^6.8.1",
"html-webpack-plugin": "^5.5.3",
"less": "^4.2.0",
"less-loader": "^11.1.3",
"postcss": "^8.4.31",
"postcss-loader": "^7.3.3",
"postcss-preset-env": "^9.3.0",
"style-loader": "^3.3.3",
"ts-loader": "^9.5.1",
"typescript": "^5.3.2",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
}
- tsconfig.json
{
"compilerOptions": {
"module": "ES6",
"target": "ES6",
"strict": true,
"noEmitOnError": true
},
}
- webpack.config.js
// 引入一个包
const path = require('path')
const HTMlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// webpack中的所有的配置信息都应该写在moudle.exports中
module.exports = {
// 当前为开发模式
mode: 'development',
// 指定入口文件
entry: "./src/index.ts",
// 打包文件的设置项
output: {
// 打包后文件的目录
path: path.resolve(__dirname,'dist'),
// 打包后文件的名字
filename: "bundle.js",
// 打包后文件不使用箭头函数,不使用const
environment: {
arrowFunction: false,
const:false
},
},
// 指定webpack打包时要使用的模块
module: {
// 指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
// 要使用的loader
use: [
// 将新版本的js转换为旧版本的js,提高代码的兼容性
{
// 指定加载器
loader:'babel-loader',
// 设置上面这个加载器的配置项
options: {
// 设置预定义的环境(代码要在那些浏览器中运行)
presets: [
[
'@babel/preset-env',
{
// 要兼容的目标浏览器
targets:{
// 'chrome':'88',
'ie':'10'
},
// 指定corejs的版本
'corejs':'3',
// 使用corejs的方式:usage 按需加载
'useBuiltIns':'usage'
}
]
]
}
},
'ts-loader', // 将ts转换为js
],
// 要排除的文件
exclude: /node_moudles/
},
// 设置less文件的处理
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions:{
plugins: [
[
"postcss-preset-env",
{
browsers:"last 2 versions"
}
]
]
}
}
},
"less-loader"
]
}
]
},
// 插件
plugins: [
// 自动生成html文件,并且引入相关资源
new HTMlWebpackPlugin({
// 自动生成的网页以路径中的网页为模板
template: "./src/index.html"
}),
// 自动清除上次编译后的文件
new CleanWebpackPlugin(),
],
// 设置那些文件可以作为模块可以被引用
resolve: {
extensions: ['.ts','.js']
}
}