项目构建
新建项目或者修改之前的项目,删去没用的代码,只留下之前写的几个配置文件 如果是新建项目,可以把 package.json,tsconfig.json,webpack.config.js 拿到根目录,然后执行 npm i 来下载模块
src 目录下新建 index.ts 和 index.html,ts 文件中随便写点东西,执行 npm run build ,如果生成了 dist 文件即为构建成功了 Tips :右键 package.json - show npm scripts 就可以展示设置的命令,双击即可执行,和上边结果一样的
安装插件 less
Less 是一个Css 预编译器,意思指的是它可以扩展Css语言,添加功能如允许变量(variables),混合(mixins),函数(functions) 和许多其他的技术,让你的Css更具维护性,主题性,扩展性
①、安装 cnpm i -D less less-loader css-loader style-loader ②、配置
......
//webpack 中的所有配置都应该写在 module.exports 中
module.exports = {
......
//指定webpack打包时使用的模块
module: {
//指定要加载的规则
rules: [
{
......
//要排除的文件
exclude: /node_modules/
},
//设置less文件的处理
{
test: /\.less$/,
use:[
"style-loader",
"css-loader",
"less-loader"
]
}
]
},
//配置webpack插件
......
};
接下来验证下能否成功使用,在 src 中新建 style 文件夹,在其中 增加一个 index.less (新建 style sheet,选择 less即可) 构建,再运行,可以看到我们的网页已经修改了颜色了
安装插件 postcss
postcss 是一个通过 js 插件来转换 css 的工具,通过这些插件可以支持变量和混合,可以通过追加浏览器前缀生成兼容性的样式
①、安装 cnpm i -D postcss postcss-loader postcss-preset-env
less 把代码转换成 css 之后,应立刻处理兼容性,然后再交给 css-loader 处理,所以应该放在 less-loader 之后,css-loader 之前
//引入一个包
const path = require('path')
//引入插件
const HTMLWebpackPlugin = require("html-webpack-plugin")
//引入clean插件
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
//webpack 中的所有配置都应该写在 module.exports 中
module.exports = {
module: {
......
//设置less文件的处理
{
test: /\.less$/,
use:[
"style-loader",
"css-loader",
//引入postloader
{
loader: "postcss-loader",
options:{
postcssOptions:{
plugins: [
[
"postcss-preset-env",
{
browsers:"last 2 versions"
}
]
]
}
}
},
"less-loader"
]
}
]
},
......
};
创建界面
index.less
//设置变量
@bg-color: #b7d4a8;
//清除默认样式
*{
margin: 0;
padding: 0;
//改变盒子模型的计算方法
//https://developer.mozilla.org/zh-CN/docs/Web/CSS/box-sizing
box-sizing: border-box;
}
body{
font: bold 20px "Courier New";
}
//设置主窗口样式
#main {
width: 360px;
height: 420px;
background-color: @bg-color;
margin: 100px auto;
border: 10px solid black;
border-radius: 10px;
//开启弹性盒模型
display: flex;
//设置主轴方向
flex-flow: column;
//设置侧轴对齐方向
align-items: center;
//设置主轴对其方向
justify-content: space-around;
//游戏舞台
#stage {
width: 304px;
height: 304px;
border: 2px solid black;
position: relative;
//设置蛇的样式
#snake{
//id snake下的div的样式
&>div{
width: 10px;
height: 10px;
background-color: black;
border: 1px solid @bg-color;
position: absolute;
}
}
//设置食物
#food{
width: 10px;
height: 10px;
position: absolute;
left: 40px;
top: 100px;
display: flex;
//横向主轴 换行
flex-flow: row wrap;
//空白空间分配到元素之间
justify-content: space-between;
align-content: space-between;
&>div{
width: 4px;
height: 4px;
background-color: black;
//旋转
transform: rotate(45deg);
}
}
}
//游戏积分牌
#score-panel {
width: 300px;
display: flex;
//设置主轴对其方向
justify-content: space-around;
}
}
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>
创建食物类
修改 index.ts
import './style/index.less';
//定义食物类
class Food {
//食物对应的元素
element: HTMLElement;
constructor() {
//获取页面中 food 元素
//最后加一个叹号表示不会为空,否则 this.element 有为空警告
this.element = document.getElementById('food')!;
}
//获取食物x轴坐标的方法
get X() {
return this.element.offsetLeft;
}
//获取食物y轴坐标的方法
get Y() {
return this.element.offsetTop;
}
//修改食物位置的方法
change() {
//生成一个随机的位置
//食物的位置最小是0,最大是290
//蛇移动一次是1格 也就是10,所以蛇的位置是整10的
//Math.random()生成0-1的随机数(不包括0和1)
//* 29 也就会生成0-29的随机数(不包括0和29)
//Math.round进行四舍五入取整,这样就可以包括0和29
//然后整体 * 10 就得到10的整数倍了
let left = Math.round(Math.random() * 29) * 10;
let top = 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);
food.change();
运行代码,这样每次刷新页面食物的位置都会改变
创建记分牌类
修改 index.ts
//记分牌类
class ScorePanel{
//记录分数
score=0;
//记录等级
level=1;
scoreEle:HTMLElement;
levelEle:HTMLElement;
//设置一个变量限制等级
maxLevel:number;
//设置一个变量限制多少分升级
upScore:number;
//maxLevel默认10,如果不传就是10
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+"";
//每10分升级
if(this.score % this.upScore === 0){
this.levelUp();
}
}
//提升等级的方法
levelUp(){
if(this.level < this.maxLevel) {
this.levelEle.innerHTML = ++this.level + "";
}
}
}
//测试代码
const scorePanel = new ScorePanel();
for(let i=0;i<10;i++){
scorePanel.addScore();
}
分模块
src 下新建 moduls 文件夹,再新建 Food.ts 和 ScorePanel.ts,分别把之前写的代码粘进来 不过两个文件都需要在最后分别加上:
//做为默认模块暴露出来
export default Food;
//做为默认模块暴露出来
export default ScorePanel;
蛇类
先初步写一个 蛇的类,后边再继续完善
class Snake {
//表示蛇头的元素
head: HTMLElement;
//蛇的身体(包括蛇头),HTMLCollection 是一个接口,表示 HTML 元素的集合
// HTMLCollection 是“活”的;如果基本的文档改变时,那些改变通过所有 HTMLCollection 对象会立即显示出来。
bodies: HTMLCollection;
//获取蛇的容器
element: HTMLElement;
constructor() {
this.element = document.getElementById("snake")!;
//querySelector() 方法返回文档中匹配指定 CSS 选择器的一个元素。只获取第一个
this.head = document.querySelector("#snake > div") as HTMLElement;
this.bodies = this.element.getElementsByTagName("div");
}
//获取蛇头的坐标
get X() {
return this.head.offsetLeft;
}
get Y() {
return this.head.offsetTop;
}
//设置蛇头的坐标
set X(value: number) {
this.head.style.left = value + "px";
}
set Y(value: number) {
this.head.style.top = value + "px";
}
//蛇的身体增加的方法
addBody() {
//向element增加一个div
//insertAdjacentHTML() 将指定的文本解析为HTML或XML,并将结果节点插入到DOM树中的指定位置
//语法:element.insertAdjacentHTML(position, text);
//beforeend: 插入元素内部的最后一个子节点之后。
this.element.insertAdjacentHTML("beforeend", "<div></div>");
}
}
//做为默认模块暴露出来
export default Snake;
键盘类
我们先简单写一个键盘类,然后调用看看
//引入其他类
import Snake from "./Snake";
import ScorePanel from "./ScorePanel";
import Food from "./Food";
//游戏控制器,控制其他所有类
class GameControl {
//定义3个属性
snake: Snake;
food: Food;
scorePanel: ScorePanel;
//创建一个属性来存储蛇的移动方向,也就是按键的方向
direction: string = '';
constructor() {
this.snake = new Snake();
this.food = new Food();
this.scorePanel = new ScorePanel();
this.init();
}
//游戏初始化
init() {
//绑定键盘按下事件
document.addEventListener('keydown', this.keyDownHandler)
}
/**
* Chrome:
* ArrowUp/ArrowDown/ArrowLeftArrowRight
* IE:
* Up/Down/Left/Right
*/
//创建键盘按下响应函数
keyDownHandler(event: KeyboardEvent) {
//console.log(event.key);
//注意这里的this是init方法中调用这个方法的document
this.direction = event.key;
}
}
export default GameControl;
在 GameControl 的 init() 方法绑定了键盘按下事件,然后用一个变量 direction 接收,我们在 index.ts 中打印看下结果 在 index.ts 中调用
import './style/index.less';
import GameControl from "./moduls/GameControl";
const gc = new GameControl();
setInterval(()=>{
console.log(gc.direction);
},1000);
发现并没有拿到 direction 的值,这是因为 keyDownHandler() 方法改变 direction 的值时,this 表示的是init方法中调用这个方法的document,
TypeScript 中如果在回调函数中用了this 的话, 就要小心了, 这个 this 不一定是指向当前类对象了,如果想确保指向的还是那个对象的话, 需要在传递那个方法的时候, 先调用 bind(this)
所以我们修改 init
init() {
//绑定键盘按下事件
document.addEventListener('keydown', this.keyDownHandler.bind(this))
}
学习更多:详解Typescript里的This
再运行就可以得到方向了
|