Fork me on GitHub

Promise技术学这篇就够了

Promise技术学这篇就够了

一. Promise是什么?

Promise 是异步编程的一种解决方案,比传统的解决方案回调函数, 更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象 。

  • 指定回调函数方式更灵活易懂。

  • 解决异步 回调地狱 的问题。

1. 回调地狱

  • 当一个回调函数嵌套一个回调函数的时候

  • 就会出现一个嵌套结构

  • 当嵌套的多了就会出现回调地狱的情况

  • 比如我们发送三个 ajax 请求

    • 第一个正常发送
    • 第二个请求需要第一个请求的结果中的某一个值作为参数
    • 第三个请求需要第二个请求的结果中的某一个值作为参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ajax({
    url: '我是第一个请求',
    success (res) {
    // 现在发送第二个请求
    ajax({
    url: '我是第二个请求'
    data: { a: res.a, b: res.b },
    success (res2) {
    // 进行第三个请求
    ajax({
    url: '我是第三个请求',
    data: { a: res2.a, b: res2.b },
    success (res3) {
    console.log(res3)
    }
    })
    }
    })
    }
    })
  • 回调地狱,其实就是回调函数嵌套过多导致的

  • 当代码成为这个结构以后,已经没有维护的可能了

二. Promise使用

  • 语法:

    1
    2
    3
    4
    5
    6
    7
    8
    new Promise(function (resolve, reject) {
    // resolve 表示成功的回调
    // reject 表示失败的回调
    }).then(function (res) {
    // 成功的函数
    }).catch(function (err) {
    // 失败的函数
    })

三. Promise 对象的状态

Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。

1
2
3
异步操作未完成(pending)
异步操作成功(fulfilled)
异步操作失败(rejected)

这三种的状态的变化途径只有两种。

1
2
从“未完成”到“成功”
从“未完成”到“失败”

一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。

因此,Promise 的最终结果只有两种。

1
2
异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nyb6AdTk-1680683483530)(%E7%AC%94%E8%AE%B0.assets/image-20220902141409899.png)]

四.Promise对象方法

Promise 是一个对象,也是一个构造函数。

1.Promise.resolve

将现有对象转为 Promise 对象

1
2
3
Promise.resolve('kerwin')
// 等价于
new Promise(resolve => resolve('kerwin'))

2.Promise.reject

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

1
2
3
const p = Promise.reject('error');
// 等同于
const p = new Promise((resolve, reject) => reject('error'))

3.Promise.all

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

1
const p = Promise.all([p1, p2, p3]);

p的状态由p1,p2,p3 决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

4.Promise.race

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

1
const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

5.Promise.allSettled

Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。

1
2
3
4
5
6
7
8
9
const promises = [ ajax('/200接口'), ajax('/401接口') ];

Promise.allSettled(promises).then(results=>{
// 过滤出成功的请求
results.filter(item =>item.status === 'fulfilled');
过滤出失败的请求
results.filter(item=> item.status === 'rejected');
})

6.Promise.any

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

Promise.any()Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

五.手写Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
function KerwinPromise(executor) {
this.status = "pending";
this.result = undefined;
this.cb = []
var _this = this;

function resolve(res) {
if (_this.status !== "pending") return;
// console.log(_this)
_this.status = "fulfilled"
_this.result = res;

_this.cb.forEach(item => {
item.successCB && item.successCB(_this.result)
});
}

function reject(res) {
if (_this.status !== "pending") return;
// console.log("reject")
_this.status = "rejected"
_this.result = res;
_this.cb.forEach(item => {
item.failCB && item.failCB(_this.result)
});
}
executor(resolve, reject)
}

KerwinPromise.prototype.then = function (successCB, failCB) {

if(!successCB){
successCB = value=>value
}
if(!failCB){
failCB = error=>error
}

// successCB()
return new KerwinPromise((resolve, reject) => {
if (this.status === "fulfilled") {
var result = successCB && successCB(this.result)
// console.log(result);

if (result instanceof KerwinPromise) {
result.then(res => {
// console.log(res)
resolve(res);
}, err => {
// console.log(err)
reject(err)
})
} else {
resolve(result);
}
}
if (this.status === "rejected") {
var result = failCB && failCB(this.result)

if (result instanceof KerwinPromise) {
result.then(res => {
// console.log(res)
resolve(res);
}, err => {
// console.log(err)
reject(err)
})
} else {
reject(result);
}
}

if (this.status === "pending") {
//收集回调
this.cb.push({
successCB: () => {
var result = successCB && successCB(this.result)

if (result instanceof KerwinPromise) {
result.then(res => {
// console.log(res)
resolve(res);
}, err => {
// console.log(err)
reject(err)
})
} else {
resolve(result);
}
},
failCB: () => {
var result = failCB && failCB(this.result)
if (result instanceof KerwinPromise) {
result.then(res => {
// console.log(res)
resolve(res);
}, err => {
// console.log(err)
reject(err)
})
} else {
reject(result);
}
}
})
}
})
}

KerwinPromise.prototype.catch= function(failCB){
this.then(undefined,failCB)
}

六.Async与Await

1.Async

async 函数,使得异步操作变得更加方便。

  • 更好的语义。
  • 返回值是 Promise。
1
2
3
4
async function test(){

}
test()

2.Await

await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

1
2
3
4
5
6
7
8
9
10
11
async function test(){
var res1 = await ajax("http://localhost:3000/news1")
var res2 = await ajax("http://localhost:3000/news2")
return res2
}

test().then(res=>{
console.log("返回结果",res)
}).catch(err=>{
console.log("err",err)
})

3.错误处理

1
2
3
4
5
6
try{
var res1 = await ajax("http://localhost:3000/news1")
var res2 = await ajax("http://localhost:3000/news2")
}catch(err){
console.log("err",err)
}

如果对你有帮助,点赞、收藏、关注是我更新的动力!👋🌟🚀

打字通小游戏制作教程:用HTML5和JavaScript提升打字速度

打字通小游戏制作教程:用HTML5和JavaScript提升打字速度

在这篇文章中,我们将一起学习如何使用HTML5和JavaScript来制作一个简单的打字通小游戏。这个小游戏可以帮助用户练习打字速度和准确性。通过这个教程,你将了解如何创建游戏界面、处理用户输入、实现倒计时以及计算得分。即使你是编程新手,也能跟随步骤完成这个项目。

体验地址

洛可可白⚡️打字通
在这里插入图片描述

准备工作

首先,确保你的计算机上安装了文本编辑器,如Notepad++、Sublime Text或Visual Studio Code。这些工具将帮助你编写和编辑代码。

创建HTML结构

打开你的文本编辑器,创建一个新的HTML文件,并输入以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!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>
<style>
/* 在这里添加CSS样式 */
</style>
</head>
<body>
<div class="bigBox">
<div class="container">你准备好了吗?</div>
<textarea placeholder="开始输入..." style="resize: none" cols="30" rows="10"></textarea>
<div class="operate">
<button>开始</button>
<div id="timer">60</div>
</div>
</div>
<script>
// 在这里添加JavaScript代码
</script>
</body>
</html>

这是我们游戏的基本结构。<head>部分包含了页面的元数据和样式定义,<body>部分则是游戏的主要内容。

添加CSS样式

<style>标签内,我们将添加一些CSS样式来美化我们的打字通游戏。这包括游戏容器、文本区域、按钮和计时器的样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
-o-user-select: none;
user-select: none;
}

.bigBox {
width: 50%;
background-color: #ac8c3e;
margin: 40px auto;
box-sizing: border-box;
padding: 20px;
border-radius: 30px;
box-shadow: 0px 0px 30px 9px #939393;
}

.container {
margin: 0 auto;
text-align: center;
padding: 20px;
}

textarea {
width: 100%;
height: 200px;
margin: 20px 0;
font-size: 20px;
border: none;
}

.operate {
width: 20%;
margin: 0 auto;
text-align: center;
}

button {
font-size: 24px;
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}

#timer {
font-size: 48px;
margin: 20px;
}

编写JavaScript逻辑

现在,我们将在<script>标签内添加JavaScript代码,这是游戏的核心部分。我们将创建游戏文本、初始化游戏、处理用户输入、实现倒计时以及计算得分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const text = "Believe in yourself and all that you are..."; // 游戏文本
const container = document.querySelector(".container");
const input = document.querySelector("textarea");
const button = document.querySelector("button");
const timer = document.getElementById("timer");
input.value = "";

let countdown;

function startGame() {
// 游戏开始后,禁用按钮
button.disabled = true;

// 显示文本
container.textContent = text;

// 启动倒计时
countdown = setInterval(() => {
const remainingTime = parseInt(timer.textContent) - 1;
if (remainingTime === 0) {
// 时间用完,游戏结束
endGame();
}

timer.textContent = remainingTime;
}, 1000);
}

function endGame() {
// 停止倒计时
clearInterval(countdown);

// 计算得分
const score = calculateScore();
const scoreMessage = `你的得分是 ${score} 分!`;
container.textContent = scoreMessage;

button.disabled = false;
}

function calculateScore() {
const userText = input.value.trim();
const correctText = text.trim();
const userWords = userText.split(" ");
const correctWords = correctText.split(" ");
let score = 0;

for (let i = 0; i < userWords.length; i++) {
if (userWords[i] === correctWords[i]) {
score++;
}
}

return score;
}

// 添加按钮点击事件监听器
button.addEventListener("click", () => {
// 设置倒计时时间
timer.textContent = "60";

// 清空输入框和输出文本区域
input.value = "";
container.textContent = "";

// 启动游戏
startGame();
});

在这个脚本中,我们首先定义了游戏的文本。然后,我们创建了开始游戏的函数startGame,它将显示游戏文本并启动倒计时。我们还定义了结束游戏的函数endGame,它将停止倒计时并计算得分。calculateScore函数用于计算用户的得分。最后,我们为开始按钮添加了一个点击事件监听器,当用户点击按钮时,游戏将开始。

测试游戏

保存你的HTML文件,并在浏览器中打开它。你应该能看到一个打字通游戏的界面。点击“开始”按钮,游戏将开始,你可以尝试在限定时间内尽可能准确地输入显示的文本。时间结束后,你的得分将被计算出来。

全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<!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>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
-moz-user-select: none;
/*火狐*/
-webkit-user-select: none;
/*webkit浏览器*/
-ms-user-select: none;
/*IE10*/
-khtml-user-select: none;
/*早期浏览器*/
-o-user-select: none;
user-select: none;
}

.bigBox {
width: 50%;
background-color: #ac8c3e;
margin: 40px auto;
box-sizing: border-box;
padding: 20px;
border-radius: 30px;
box-shadow: 0px 0px 30px 9px #939393;
}

.container {
margin: 0 auto;
text-align: center;
padding: 20px;
}

textarea {
width: 100%;
height: 200px;
margin: 20px 0;
font-size: 20px;
border: none;
}

.operate {
width: 20%;
margin: 0 auto;
text-align: center;
}

button {
font-size: 24px;
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}

#timer {
font-size: 48px;
margin: 20px;
}
</style>
</head>

<body>
<div class="bigBox">
<div class="container">你准备好了吗?</div>
<textarea
name=""
placeholder="开始输入..."
id=""
style="resize: none"
cols="30"
rows="10"
></textarea>
<div class="operate">
<button>开始</button>
<div id="timer">60</div>
</div>
</div>

<script>
const text =
"Believe in yourself and all that you are. Know that there is something inside you that is greater than any obstacle. This quote by Christian D. Larson reminds us that we all have the power within us to overcome any obstacle we may face. When we have confidence in ourselves and our abilities, we can achieve great things. So, let's trust ourselves, believe in our dreams, and work hard to make them a reality.";

const container = document.querySelector(".container");
const input = document.querySelector("textarea");
const button = document.querySelector("button");
const timer = document.getElementById("timer");
input.value = "";

let countdown;

function startGame() {
// 游戏开始后,禁用按钮
button.disabled = true;

// 显示文本
container.textContent = text;

// 启动倒计时
countdown = setInterval(() => {
const remainingTime = parseInt(timer.textContent) - 1;
if (remainingTime === 0) {
// 时间用完,游戏结束
endGame();
}

timer.textContent = remainingTime;
}, 1000);
}

function endGame() {
// 停止倒计时
clearInterval(countdown);

// 计算得分
const score = calculateScore();
const scoreMessage = `你的得分是 ${score} 分!`;
container.textContent = scoreMessage;

button.disabled = false;
}

function calculateScore() {
const userText = input.value.trim();
const correctText = text.trim();
const userWords = userText.split(" ");
const correctWords = correctText.split(" ");
let score = 0;

for (let i = 0; i < userWords.length; i++) {
console.log(userWords[i], correctWords[i]);
if (userWords[i] === correctWords[i]) {
score++;
}
}

return score;
}

// 添加按钮点击事件监听器
button.addEventListener("click", () => {
// 设置倒计时时间
timer.textContent = "60";

// 清空输入框和输出文本区域
input.value = "";
container.textContent = "";

// 启动游戏
startGame();
});
</script>
</body>
</html>

🎉 结语

恭喜你,你已经成功创建了一个打字通小游戏!这个教程涵盖了从创建基本的HTML结构到添加CSS样式,再到编写JavaScript交互逻辑的全过程。通过这个项目,你不仅学会了如何制作一个小游戏,还对前端开发有了基本的了解。随着你技能的提升,你可以尝试添加更多的功能,比如记录用户的最佳得分、添加音效或者实现更复杂的游戏逻辑。祝你编程愉快!
如果对你有帮助,点赞、收藏、关注是我更新的动力!👋🌟🚀

扫雷小游戏制作教程:用HTML5和JavaScript打造经典游戏

扫雷小游戏制作教程:用HTML5和JavaScript打造经典游戏

在这篇文章中,我们将一起学习如何使用HTML5和JavaScript来制作一个经典的扫雷小游戏。通过这个教程,你将了解如何创建游戏界面、处理用户交互以及实现游戏逻辑。即使你是编程新手,也能跟随步骤完成这个项目。

体验地址

洛可可白⚡️扫雷
在这里插入图片描述

准备工作

首先,确保你的计算机上安装了文本编辑器,如Notepad++、Sublime Text或Visual Studio Code。这些工具将帮助你编写和编辑代码。

创建HTML结构

打开你的文本编辑器,创建一个新的HTML文件,并输入以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!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>
<style>
/* 在这里添加CSS样式 */
</style>
</head>
<body>
<div class="bigBox">
<div id="controls">
<form>
<label for="level">难度级别:</label>
<select id="level">
<option value="easy">简单</option>
<option value="medium">中等</option>
<option value="hard">困难</option>
</select>
<button id="reset">重新开始</button>
</form>
</div>

<table id="board"></table>
</div>
<script>
// 在这里添加JavaScript代码
</script>
</body>
</html>

这是我们游戏的基本结构。<head>部分包含了页面的元数据和样式定义,<body>部分则是游戏的主要内容。

添加CSS样式

<style>标签内,我们将添加一些CSS样式来美化我们的扫雷游戏。这包括游戏布局、控制面板和表格样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* 游戏布局样式 */
.bigBox {
background-color: rgb(163, 159, 159);
width: 40%;
margin: 5% auto;
text-align: center;
padding: 20px;
}

#reset {
width: 100px;
font-size: 15px;
}

table {
border-collapse: collapse;
margin: 30px auto;
}

td {
width: 30px;
height: 30px;
text-align: center;
vertical-align: middle;
border: 1px solid #ccc;
}

button {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
font-size: 16px;
font-weight: bold;
color: #fff;
background-color: #333;
border: none;
}

编写JavaScript逻辑

现在,我们将在<script>标签内添加JavaScript代码,这是游戏的核心部分。我们将创建游戏参数配置、初始化游戏、处理用户点击事件、检查游戏胜利条件等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 游戏参数配置
const config = {
easy: {
rows: 8,
cols: 8,
mines: 10,
},
medium: {
rows: 10,
cols: 10,
mines: 20,
},
hard: {
rows: 12,
cols: 12,
mines: 30,
},
};

// 初始化游戏
function init() {
// ...(省略代码以节省空间,详见原代码)
}

// 用户点击格子的处理函数
function clickCell(row, col) {
// ...(省略代码以节省空间,详见原代码)
}

// 更新地雷数目显示
function updateMinesCount() {
// ...(省略代码以节省空间,详见原代码)
}

// 显示游戏结束
function showGameOver(win) {
// ...(省略代码以节省空间,详见原代码)
}

// 检查游戏是否胜利
function checkWin() {
// ...(省略代码以节省空间,详见原代码)
}

// 初始化游戏
init();

在这个脚本中,我们首先定义了游戏的难度级别配置,然后创建了初始化游戏的函数init。我们还定义了处理用户点击事件的函数clickCell,更新地雷数目的函数updateMinesCount,显示游戏结束的函数showGameOver,以及检查游戏胜利条件的函数checkWin。最后,我们调用init函数来初始化游戏。

测试游戏

保存你的HTML文件,并在浏览器中打开它。你应该能看到一个扫雷游戏的界面。选择难度级别后,点击格子开始游戏。如果你踩到地雷,游戏会结束;如果你成功避开所有地雷,恭喜你,你赢了!

全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
<!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>
<style>
/* 游戏布局样式 */
.bigBox {
background-color: rgb(163, 159, 159);
width: 40%;
margin: 5% auto;
text-align: center;
padding: 20px;
}

#reset {
width: 100px;
font-size: 15px;
}

table {
border-collapse: collapse;
margin: 30px auto;
}

td {
width: 30px;
height: 30px;
text-align: center;
vertical-align: middle;
border: 1px solid #ccc;
}

button {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
font-size: 16px;
font-weight: bold;
color: #fff;
background-color: #333;
border: none;
}

/* 控制面板样式 */
#controls {
margin-top: 20px;
}
</style>
</head>

<body>
<div class="bigBox">
<div id="controls">
<form>
<label for="level">难度级别:</label>
<select id="level">
<option value="easy">简单</option>
<option value="medium">中等</option>
<option value="hard">困难</option>
</select>
<button id="reset">重新开始</button>
</form>
</div>

<table id="board"></table>
</div>
</body>

<script>
// 游戏参数配置
const config = {
easy: {
rows: 8,
cols: 8,
mines: 10,
},
medium: {
rows: 10,
cols: 10,
mines: 20,
},
hard: {
rows: 12,
cols: 12,
mines: 30,
},
};
// 初始化游戏
let board = document.getElementById("board");
let level = document.getElementById("level");
let reset = document.getElementById("reset");
let cells = [];
let gameover = false;
let minesLeft = 0;
let minesCount = 0;
let rows, cols, mines;

reset.addEventListener("click", init);

level.addEventListener("change", function () {
init();
});

function init() {
// 初始化游戏参数
let levelConfig = config[level.value];
rows = levelConfig.rows;
cols = levelConfig.cols;
mines = levelConfig.mines;
minesLeft = mines;
minesCount = 0;
gameover = false;
// 初始化游戏布局
board.innerHTML = "";
cells = [];
for (let i = 0; i < rows; i++) {
let row = [];
let tr = document.createElement("tr");
for (let j = 0; j < cols; j++) {
let td = document.createElement("td");
let button = document.createElement("button");
button.addEventListener("click", function () {
if (!gameover) {
clickCell(i, j);
}
});
td.appendChild(button);
tr.appendChild(td);
row.push({ button: button, hasMine: false, revealed: false });
}
cells.push(row);
board.appendChild(tr);
}
// 初始化地雷
for (let i = 0; i < mines; i++) {
let row, col;
do {
row = Math.floor(Math.random() * rows);
col = Math.floor(Math.random() * cols);
} while (cells[row][col].hasMine);
cells[row][col].hasMine = true;
}
// 更新地雷数目显示
updateMinesCount();
}

function clickCell(row, col) {
let cell = cells[row][col];
if (cell.revealed) {
return;
}
if (cell.hasMine) {
revealMines();
showGameOver(false);
return;
}
cell.revealed = true;
cell.button.style.backgroundColor = "#ddd";
let minesAround = countMinesAround(row, col);
if (minesAround > 0) {
cell.button.textContent = minesAround;
} else {
revealNeighbors(row, col);
}
if (checkWin()) {
showGameOver(true);
}
}

function revealNeighbors(row, col) {
for (let i = row - 1; i <= row + 1; i++) {
for (let j = col - 1; j <= col + 1; j++) {
if (
i >= 0 &&
i < rows &&
j >= 0 &&
j < cols &&
!(i == row && j == col)
) {
clickCell(i, j);
}
}
}
}

function countMinesAround(row, col) {
let count = 0;
for (let i = row - 1; i <= row + 1; i++) {
for (let j = col - 1; j <= col + 1; j++) {
if (i >= 0 && i < rows && j >= 0 && j < cols && cells[i][j].hasMine) {
count++;
}
}
}
return count;
}

function revealMines() {
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (cells[i][j].hasMine) {
cells[i][j].button.style.backgroundColor = "#f00";
}
}
}
}

function updateMinesCount() {
console.log("这是哈哈", minesLeft);
// minesCountElem.textContent = minesLeft;
}

function showGameOver(win) {
gameover = true;
let message = win ? "You Win!" : "You Lose!";
alert(message);
}

function checkWin() {
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
let cell = cells[i][j];
if (!cell.hasMine && !cell.revealed) {
return false;
}
}
}
return true;
}

init();
</script>
</html>

结语

恭喜你,你已经成功创建了一个扫雷小游戏!这个教程涵盖了从创建基本的HTML结构到添加CSS样式,再到编写JavaScript交互逻辑的全过程。通过这个项目,你不仅学会了如何制作一个小游戏,还对前端开发有了基本的了解。随着你技能的提升,你可以尝试添加更多的功能,比如计时器、得分系统或者更复杂的游戏逻辑。祝你编程愉快!

拼图小游戏制作教程:用HTML5和JavaScript打造经典游戏

拼图小游戏制作教程:用HTML5和JavaScript打造经典游戏

在这篇文章中,我们将一起学习如何从头开始制作一个简单的拼图小游戏。我们将使用HTML5和JavaScript来创建这个小游戏,不需要任何复杂的框架或库。通过这个教程,你将了解基本的网页布局、CSS样式设置以及JavaScript的交互逻辑。

在线体验

洛可可白⚡️拼图
在这里插入图片描述

准备工作

首先,确保你的计算机上安装了文本编辑器,如Notepad++、Sublime Text或Visual Studio Code。这些工具将帮助你编写和编辑代码。

创建HTML结构

打开你的文本编辑器,创建一个新的HTML文件,并输入以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!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>
<style>
/* 在这里添加CSS样式 */
</style>
</head>
<body>
<div id="puzzle-container">
<div id="puzzle-board"></div>
</div>
<button id="shuffle-btn">打乱拼图</button>
<script>
// 在这里添加JavaScript代码
</script>
</body>
</html>

这是我们游戏的基本结构。<head>部分包含了页面的元数据和样式定义,<body>部分则是游戏的主要内容。

添加CSS样式

<style>标签内,我们将添加一些CSS样式来美化我们的拼图游戏。这包括拼图容器的边框、拼图块的大小和样式,以及按钮的样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#puzzle-container {
width: 300px;
height: 300px;
margin: 0 auto;
border: 2px solid #ccc;
position: relative;
overflow: hidden;
}

#puzzle-board {
width: 300px;
height: 300px;
position: absolute;
}

.puzzle-piece {
width: 100px;
height: 100px;
position: absolute;
background-size: 300px 300px;
border: 2px solid #fff;
transition: all 0.3s ease-in-out;
}

button {
display: block;
margin: 20px auto;
}

编写JavaScript逻辑

现在,我们将在<script>标签内添加JavaScript代码,这是游戏的核心部分。我们将创建拼图块,处理用户交互,并实现打乱拼图的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
document.addEventListener("DOMContentLoaded", () => {
// 获取DOM元素
const puzzleContainer = document.getElementById("puzzle-container");
const puzzleBoard = document.getElementById("puzzle-board");
const shuffleButton = document.getElementById("shuffle-btn");
const imageSrc = "http://example.com/image.jpg"; // 替换为你的图片地址
const rows = 3;
const cols = 3;
const pieces = [];

// 创建拼图块的函数
function createPuzzlePieces() {
// ...(省略代码以节省空间,详见原代码)
}

// 移动拼图块的函数
function movePiece(piece) {
// ...(省略代码以节省空间,详见原代码)
}

// 检查是否完成拼图的函数
function checkWin() {
// ...(省略代码以节省空间,详见原代码)
}

// 打乱拼图的函数
function shufflePuzzle() {
// ...(省略代码以节省空间,详见原代码)
}

// 初始化游戏
createPuzzlePieces();
shuffleButton.addEventListener("click", shufflePuzzle);
});

在这个脚本中,我们首先监听文档加载完成的事件,然后获取页面上的元素。我们定义了几个函数来创建拼图块、移动拼图块、检查游戏胜利条件以及打乱拼图。最后,我们初始化游戏并为打乱按钮添加事件监听器。

测试游戏

保存你的HTML文件,并在浏览器中打开它。你应该能看到一个拼图游戏的界面。点击“打乱拼图”按钮,拼图块会被随机打乱。你可以通过拖动拼图块来完成拼图。

全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<!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>
<style>
#puzzle-container {
width: 300px;
height: 300px;
margin: 0 auto;
border: 2px solid #ccc;
position: relative;
overflow: hidden;
}

#puzzle-board {
width: 300px;
height: 300px;
position: absolute;
}

.puzzle-piece {
width: 100px;
height: 100px;
position: absolute;
background-size: 300px 300px;
border: 2px solid #fff;
transition: all 0.3s ease-in-out;
}

button {
display: block;
margin: 20px auto;
}
</style>
</head>

<body>
<div id="puzzle-container">
<div id="puzzle-board"></div>
</div>
<button id="shuffle-btn">打乱拼图</button>
</body>
<script>
document.addEventListener("DOMContentLoaded", () => {
const puzzleContainer = document.getElementById("puzzle-container");
const puzzleBoard = document.getElementById("puzzle-board");
const shuffleButton = document.getElementById("shuffle-btn");
const imageSrc =
"http://cdn-hw-static2.shanhutech.cn/bizhi/staticwp/202312/d989b0fbf30d985ee89f15ef2fd640db--2492230555.jpg"; // 替换为你的图片地址
const rows = 3;
const cols = 3;
const pieces = [];

let emptyPiece;
let isShuffling = false;

// 创建拼图块
function createPuzzlePieces() {
const pieceWidth = puzzleContainer.offsetWidth / cols;
const pieceHeight = puzzleContainer.offsetHeight / rows;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const piece = document.createElement("div");
piece.className = "puzzle-piece";
piece.style.width = `${pieceWidth}px`;
piece.style.height = `${pieceHeight}px`;
piece.style.backgroundImage = `url(${imageSrc})`;
piece.style.backgroundPosition = `${-col * pieceWidth}px ${
-row * pieceHeight
}px`;
piece.style.top = `${row * pieceHeight}px`;
piece.style.left = `${col * pieceWidth}px`;
piece.dataset.row = row;
piece.dataset.col = col;

if (row === rows - 1 && col === cols - 1) {
emptyPiece = piece;
piece.classList.add("empty");
} else {
piece.addEventListener("click", () => {
if (!isShuffling) {
movePiece(piece);
}
});
}

puzzleBoard.appendChild(piece);
pieces.push(piece);
}
}
}

// 移动拼图块
function movePiece(piece) {
const emptyPieceRow = parseInt(emptyPiece.dataset.row);
const emptyPieceCol = parseInt(emptyPiece.dataset.col);
const pieceRow = parseInt(piece.dataset.row);
const pieceCol = parseInt(piece.dataset.col);

if (
(pieceRow === emptyPieceRow &&
Math.abs(pieceCol - emptyPieceCol) === 1) ||
(pieceCol === emptyPieceCol &&
Math.abs(pieceRow - emptyPieceRow) === 1)
) {
const tempRow = emptyPieceRow;
const tempCol = emptyPieceCol;
emptyPiece.style.top = `${pieceRow * piece.offsetHeight}px`;
emptyPiece.style.left = `${pieceCol * piece.offsetWidth}px`;
emptyPiece.dataset.row = pieceRow;
emptyPiece.dataset.col = pieceCol;

piece.style.top = `${tempRow * piece.offsetHeight}px`;
piece.style.left = `${tempCol * piece.offsetWidth}px`;
piece.dataset.row = tempRow;
piece.dataset.col = tempCol;
}

checkWin();
}

let isWin = false; // 添加标志位

// 检查是否完成拼图
function checkWin() {
let isPuzzleComplete = true;
for (let i = 0; i < pieces.length; i++) {
const piece = pieces[i];
const row = parseInt(piece.dataset.row);
const col = parseInt(piece.dataset.col);
if (row !== Math.floor(i / cols) || col !== i % cols) {
isPuzzleComplete = false;
break;
}
}

if (isPuzzleComplete && !isWin && !isShuffling) {
// 添加条件判断
isWin = true; // 设置标志位为true
setTimeout(() => {
alert("恭喜!你完成了拼图!");
shuffleButton.disabled = false;
isWin = false; // 重置标志位为false
}, 200);
}
}
// 打乱拼图
function shufflePuzzle() {
isShuffling = true;
shuffleButton.disabled = true;
isWin = false; // 重置标志位为false

const shuffleCount = 100;
let count = 0;

const intervalId = setInterval(() => {
const randomIndex = Math.floor(Math.random() * pieces.length);
const randomPiece = pieces[randomIndex];
movePiece(randomPiece);
count++;

if (count >= shuffleCount) {
clearInterval(intervalId);
isShuffling = false;
shuffleButton.disabled = false;
}
}, 10);
}
createPuzzlePieces();
shuffleButton.addEventListener("click", shufflePuzzle);
});
</script>
</html>

结语

恭喜你,你已经成功创建了一个简单的拼图小游戏!这个教程涵盖了从创建基本的HTML结构到添加CSS样式,再到编写JavaScript交互逻辑的全过程。通过这个项目,你不仅学会了如何制作一个小游戏,还对前端开发有了基本的了解。随着你技能的提升,你可以尝试添加更多的功能,比如计时器、得分系统或者更复杂的拼图形状。祝你编程愉快!

排序算法全景:从基础到高级的Java实现

排序算法全景:从基础到高级的Java实现

排序算法是计算机科学中的一个基础概念,它在数据处理和信息检索中扮演着至关重要的角色。本文将通过几个简单的Java程序,带你了解几种常见的排序算法:插入排序、希尔排序、归并排序、快速排序和选择排序,以及一个用于生成和打印测试数据的工具类。

插入排序:理解排序的核心思想

什么是插入排序?

插入排序(Insertion Sort)算法是一种直观且易于理解的排序方法。插入排序的工作原理类似于我们整理扑克牌的方式。想象一下,你手中有一堆未排序的扑克牌,你将它们一张张插入到已经排序好的牌堆中。插入排序算法正是基于这样的思想:它将数组分为已排序和未排序两部分,然后逐个将未排序部分的元素插入到已排序部分的适当位置。

插入排序的Java实现

让我们通过一个Java程序来具体看看插入排序是如何工作的。这个程序定义了一个名为 _01_InsertionSort 的类,其中包含了排序方法和一些辅助函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class _01_InsertionSort {
public static void sort(Comparable[] arr) {
int n = arr.length;
for (int i = 0; i < n; i++) {
// 寻找元素arr[i]合适的插入位置
for (int j = i; j > 0; j--) {
if (arr[j].compareTo(arr[j - 1]) < 0) {
swap(arr, j, j - 1);
} else {
break;
}
}
}
}

private static void swap(Object[] arr, int i, int j) {
Object t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}

在这个类中,sort 方法接受一个可比较的对象数组 arr 作为参数。方法的核心是一个双重循环:外层循环遍历数组的每个元素,内层循环则负责将当前元素与已排序部分的元素进行比较,并在必要时进行交换。

swap 方法是一个辅助函数,用于交换数组中的两个元素。这是在内层循环中,当我们发现需要将一个元素插入到它之前的位置时调用的。

程序的执行

程序的 main 方法首先生成了一个包含20000个随机整数的数组,然后调用 sort 方法对数组进行排序,最后打印出排序后的数组。

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
int N = 20000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
_01_InsertionSort.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print(' ');
}
}

插入排序的核心思想

插入排序的核心思想是分而治之。它将排序问题分解为更小的部分,然后逐个解决。这种方法在数据量较小或者数据基本有序的情况下非常有效,因为它可以减少不必要的比较和交换操作。

结语

通过这个简单的Java程序,我们不仅学习了插入排序算法的实现,还理解了排序算法的一般思想。虽然插入排序在处理大数据集时可能不是最高效的选择,但它的简单性和直观性使其成为理解排序概念的一个良好起点。随着你对算法的深入学习,你将能够掌握更多高级的排序技术,以应对更复杂的数据处理挑战。

希尔排序:一种高效的改进版插入排序

希尔排序(Shell Sort)是一种对传统插入排序的改进,它通过引入间隔(gap)的概念来提高排序的效率。

希尔排序简介

希尔排序是由Donald Shell在1959年提出的一种排序算法。它基于插入排序,通过将原始数据集分割成若干个子序列来排序,这些子序列的元素间隔逐渐减小,最后合并为一个有序的序列。

Java实现希尔排序

让我们通过一个简单的Java程序来实现希尔排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class _02_ShellSort {
public static void sort(Comparable[] arr) {
// 初始化间隔
int gap = arr.length / 2;
// 当间隔大于0时,执行排序
while (gap > 0) {
// 遍历数组,间隔为gap
for (int i = gap; i < arr.length; i++) {
// 临时存储当前元素
Comparable tmp = arr[i];
// 对于每个间隔内的元素,进行插入排序
for (int j = i; j >= gap && tmp.compareTo(arr[j - gap]) < 0; j -= gap) {
// 将较大的元素向后移动
arr[j] = arr[j - gap];
}
// 将当前元素放到正确的位置
arr[j] = tmp;
}
// 缩小间隔,进行下一轮排序
gap /= 2;
}
}
}

在这个实现中,我们首先计算一个初始间隔 gap,然后通过一个循环来逐渐减小这个间隔。在每次循环中,我们对间隔为 gap 的元素进行插入排序。随着间隔的减小,排序的粒度逐渐变细,最终整个数组变得有序。

测试希尔排序

为了测试我们的希尔排序算法,我们在 main 方法中生成了一个包含2000个随机整数的数组,并对其进行排序。

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
int N = 2000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 10);
_02_ShellSort.sort(arr);
// 打印排序后的数组
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print(' ');
}
}

希尔排序的核心思想

希尔排序的核心在于分而治之的策略。它不是一次性对整个数组进行排序,而是先对数组的子序列进行排序,然后逐步缩小子序列的范围,直到整个数组有序。这种方法在处理部分有序的数据时特别有效,因为它可以减少不必要的比较和交换操作。

结语

通过这个Java程序,我们不仅学习了希尔排序的实现,还理解了其背后的算法思想。希尔排序是一种简单且高效的排序方法,它在某些情况下比传统的插入排序要快得多。

归并排序:优雅的分而治之艺术

归并排序(Merge Sort)是一种优雅且高效的排序方法。归并排序通过分而治之的策略,将数据集一分为二,然后递归地对这两部分进行排序,最后将它们合并成一个有序的整体。

归并排序的基本概念

归并排序的核心在于“分而治之”。这个策略涉及将一个大问题分解成小问题,解决这些小问题,然后将它们的解决方案合并。在排序的上下文中,这意味着将一个未排序的数组分成两半,分别对这两半进行排序,然后将它们合并成一个有序数组。

Java实现归并排序

让我们通过一个Java程序来实现归并排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class _03_MergeSort {
// 将arr[l...mid]和arr[mid+1...r]两部分进行归并
private static void merge(Comparable[] arr, int l, int mid, int r) {
Comparable[] aux = Arrays.copyOfRange(arr, l, r + 1);
// 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
int i = l, j = mid + 1;
for (int k = l; k <= r; k++) {

if (i > mid) { // 如果左半部分元素已经全部处理完毕
arr[k] = aux[j - l];
j++;
} else if (j > r) { // 如果右半部分元素已经全部处理完毕
arr[k] = aux[i - l];
i++;
} else if (aux[i - l].compareTo(aux[j - l]) < 0) { // 左半部分所指元素 < 右半部分所指元素
arr[k] = aux[i - l];
i++;
} else { // 左半部分所指元素 >= 右半部分所指元素
arr[k] = aux[j - l];
j++;
}
}
}
// 递归使用归并排序,对arr[l...r]的范围进行排序
private static void sort(Comparable[] arr, int l, int r) {
if (l >= r) {
return;
}
int mid = (l + r) / 2;
sort(arr, l, mid);
sort(arr, mid + 1, r);
// 对于arr[mid] <= arr[mid+1]的情况,不进行merge
// 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
if (arr[mid].compareTo(arr[mid + 1]) > 0)
merge(arr, l, mid, r);
}
public static void sort(Comparable[] arr) {
int n = arr.length;
sort(arr, 0, n - 1);
}
}

在这个类中,merge 方法负责合并两个已经排序的子数组。sort 方法是递归的核心,它将数组分成两半,然后递归地调用自身来排序这两半。最后,sort 方法提供了一个公共接口来开始排序过程。

测试归并排序

为了测试归并排序的性能,我们在 main 方法中生成了一个包含1000个随机整数的数组,并对其进行排序。

1
2
3
4
5
6
public static void main(String[] args) {
int N = 1000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
_03_MergeSort.sort(arr);
SortTestHelper.printArray(arr);
}

归并排序的核心思想

归并排序的核心在于递归和合并。递归地将数组分成更小的部分,直到每个部分只有一个元素(或没有元素),这时数组自然是有序的。然后,通过合并相邻的有序子数组,逐步构建更大的有序数组,最终得到完全有序的原始数组。

结语

归并排序是一种非常优雅的排序算法,它不仅在理论上具有优雅的数学美感,而且在实际应用中也非常高效。它的时间复杂度在最好、最坏和平均情况下都是O(n log n),这使得它在处理大型数据集时特别有用。尽管归并排序需要额外的存储空间来创建辅助数组,但它的稳定性和效率使其成为许多排序场景下的首选算法。

快速排序:分而治之的高效排序算法

快速排序(Quick Sort)是一种分而治之策略的高效排序方法。快速排序以其平均时间复杂度为O(n log n)而闻名,它在大多数情况下都能提供出色的性能。

快速排序的基本概念

快速排序的核心在于“分而治之”。这个策略涉及将一个大问题分解成小问题,解决这些小问题,然后将它们的解决方案合并。在排序的上下文中,这意味着将一个未排序的数组分成两半,然后递归地对这两半进行排序,最后将它们合并成一个有序的整体。

Java实现快速排序

让我们通过一个Java程序来实现快速排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class _04_QuickSort {
// 对arr[l...r]部分进行partition操作
// 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
private static int partition(Comparable[] arr, int l, int r){
// 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
swap( arr, l , (int)(Math.random()*(r-l+1))+l );
Comparable v = arr[l];
// arr[l+1...j] < v ; arr[j+1...i) > v
int j = l;
for( int i = l + 1 ; i <= r ; i ++ )
if( arr[i].compareTo(v) < 0 ){
j ++;
swap(arr, j, i);
}
swap(arr, l, j);
return j;
}
// 递归使用快速排序,对arr[l...r]的范围进行排序
private static void sort(Comparable[] arr, int l, int r){
if (l >= r) {
return;
}
int p = partition(arr, l, r);
sort(arr, l, p-1 );
sort(arr, p+1, r);
}
public static void sort(Comparable[] arr){
int n = arr.length;
sort(arr, 0, n-1);
}
private static void swap(Object[] arr, int i, int j) {
Object t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
// 测试 QuickSort
public static void main(String[] args) {
// Quick Sort也是一个O(nlogn)复杂度的算法
// 可以在1秒之内轻松处理100万数量级的数据
int N = 1000000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
sort(arr);
SortTestHelper.printArray(arr);
}
}

在这个类中,partition 方法负责将数组分成两部分,sort 方法是递归排序的核心,它将数组分成两半,然后递归地对这两半进行排序。swap 方法是一个辅助函数,用于交换数组中的两个元素。

快速排序的核心思想

快速排序的核心在于选择一个基准值(pivot),然后将数组分成两部分:一部分包含所有小于基准值的元素,另一部分包含所有大于基准值的元素。这个过程称为分区(partitioning)。分区操作完成后,基准值就处于其最终排序位置。然后,我们递归地对基准值左边和右边的子数组进行同样的操作,直到整个数组变得有序。

快速排序的性能

快速排序的平均时间复杂度为O(n log n),这使得它在处理大型数据集时非常高效。尽管在最坏情况下,快速排序的时间复杂度会下降到O(n^2),但通过随机选择基准值,可以大大降低这种最坏情况发生的概率。

结语

通过学习快速排序,我们不仅掌握了一种高效的排序技术,还理解了分而治之这一强大的问题解决策略。这种策略在计算机科学中有着广泛的应用,不仅仅是在排序算法中。快速排序的优雅和效率使其成为了许多排序场景下的首选算法。

选择排序:简单而直观的排序算法入门

选择排序(Selection Sort)算法是一种易于理解和实现的排序方法。选择排序的核心思想是在每一轮迭代中找到最小(或最大)的元素,并将其移动到正确的位置。

选择排序的工作原理

选择排序算法的工作原理可以概括为以下几个步骤:

  1. 假设第一个元素已经是排序好的。
  2. 在剩余的未排序元素中找到最小(或最大)的元素。
  3. 将找到的最小(或最大)元素与当前未排序的第一个元素交换位置。
  4. 重复步骤2和3,直到所有元素都被排序。

Java实现选择排序

下面是一个选择排序的Java实现示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class _07_SelectionSort {
public static void sort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n; i++) {
// 初始化最小值索引为当前位置
int minIndex = i;
// 寻找[i, n)区间里的最小值的索引
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 交换当前位置和找到的最小值位置的元素
swap(arr, i, minIndex);
}
}

private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

public static void main(String[] args) {
int N = 20000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
_07_SelectionSort.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print(' ');
}
}
}

在这个实现中,sort 方法负责执行排序过程。它首先遍历数组,然后在每次迭代中找到最小值的索引,并将其与当前位置的元素交换。swap 方法是一个辅助函数,用于交换数组中的两个元素。

选择排序的特点

选择排序的主要特点是简单。它不需要额外的存储空间(除了临时变量),并且实现起来非常直观。然而,选择排序的效率并不是最高的,它的平均和最坏情况时间复杂度都是O(n^2),这使得它在处理大型数据集时效率较低。

结语

选择排序虽然在效率上可能不如其他更高级的排序算法,但它的简单性和易于理解的特点使其成为初学者学习排序算法的良好起点。

生成测试数据和输出测试数据的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//SortTestHelper
public class SortTestHelper {
// SortTestHelper不允许产生任何实例
private SortTestHelper(){}
// 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]
public static Integer[] generateRandomArray(int n, int rangeL, int rangeR) {
assert rangeL <= rangeR;
Integer[] arr = new Integer[n];
for (int i = 0; i < n; i++)
arr[i] = new Integer((int)(Math.random() * (rangeR - rangeL + 1) + rangeL));
return arr;
}
// 打印arr数组的所有内容
public static void printArray(Object arr[]) {
for (int i = 0; i < arr.length; i++){
System.out.print( arr[i] );
System.out.print( ' ' );
}
System.out.println();
return;
}
}

感谢你的访问,期待与你在技术的道路上相遇!👋🌟🚀

打造你的HTML5打地鼠游戏:零基础入门教程

[TOC]

打造你的HTML5打地鼠游戏:零基础入门教程

简介

在这个教程中,我们将一起学习如何使用HTML、CSS和JavaScript来创建一个简单的打地鼠游戏。这不仅是一个有趣的项目,也是学习前端开发技能的绝佳方式。

体验地址
PC端体验地址: 洛可可白⚡️打地鼠
(暂时只支持键盘输入操作)

在这里插入图片描述

准备工作

确保你的开发环境已经安装了现代浏览器,如Chrome、Firefox或Safari。我们将使用这些浏览器的开发者工具来调试和优化我们的游戏。

代码编辑器我推荐 Visual Studio Code

创建游戏结构

1. HTML布局

首先,我们需要创建一个基本的HTML页面,它将包含游戏的布局和地鼠洞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!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="styles.css">
</head>
<body>
<div id="game-container">
<div class="wam-hole">
<div class="wam-mole"></div>
</div>
<div class="wam-hole">
<div class="wam-mole"></div>
</div>
<!-- 更多地鼠洞 -->
</div>
<script src="script.js"></script>
</body>
</html>

设计游戏样式

2. CSS样式

接下来,我们将使用CSS来美化我们的游戏界面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
      /* styles.css */
* {
box-sizing: border-box;
}

h1 {
text-align: center;
line-height: 30px;
}

.bigBox {
width: 60%;
height: 400px;
margin: 20px auto;
background-color: #cbbb3e;
}

.wam-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
height: 260px;
}

.wam-hole {
position: relative;
width: 100px;
height: 100px;
margin: 0 20px;
background-color: #f5732d;
}

.wam-mole {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* 地鼠 */
background-image: url("https://pic.52112.com/180516/EPS180516_57/9jagBhddHW_small.jpg");
background-size: 100% 100%;
display: none;
}

.wam-mole--up {
display: block;
}

.wam-score {
font-size: 2rem;
text-align: center;
}

.wam-message {
font-size: 1rem;
text-align: center;
margin-top: 20px;
cursor: pointer;
}

/* 你可以添加更多的CSS来美化地鼠洞和地鼠 */

实现游戏逻辑

3. JavaScript编程

现在,我们将使用JavaScript来添加游戏逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
const container = document.querySelector(".wam-container");
const scoreBoard = document.querySelector(".wam-score");
const message = document.querySelector(".wam-message");
const moles = Array.from(container.querySelectorAll(".wam-hole"));

let lastHole;
let score = 0;
let isPlaying = false;
let timeUp = false;

// 随机时间生成地鼠
function popUpMole() {
if (timeUp) return;
const time = Math.random() * (1500 - 500) + 500;
const hole = randomHole(moles);
hole.querySelector("div").classList.add("wam-mole--up");
setTimeout(() => {
hole.querySelector("div").classList.remove("wam-mole--up");
if (!timeUp) popUpMole();
}, time);
}

// 随机选择一个地鼠洞
function randomHole(holes) {
const idx = Math.floor(Math.random() * holes.length);
const hole = holes[idx];
if (hole === lastHole) return randomHole(holes);
lastHole = hole;
return hole;
}

// 点击地鼠
function whackMole(e) {
if (!e.isTrusted) return; // 防止作弊
if (!isPlaying) return;
if (!e.target.matches(".wam-mole")) return;
score++;
scoreBoard.textContent = `分数: ${score}`;
e.target.parentNode
.querySelector("div")
.classList.remove("wam-mole--up");
}
// 开始游戏
function startGame() {
score = 0;
scoreBoard.textContent = "分数: 0";
isPlaying = true;
timeUp = false;
message.textContent = "";
popUpMole();
setTimeout(() => {
isPlaying = false;
timeUp = true;
message.textContent = `一分钟您的得分是: ${score};点我再来一次!`;
}, 60000);
}

// 初始化地鼠洞
moles.forEach((mole) => mole.addEventListener("click", whackMole));
document
.querySelector(".wam-message")
.addEventListener("click", startGame);

这段代码创建了一个简单的游戏循环,每秒钟随机显示一个地鼠,并在用户点击地鼠时给予反馈。你可以根据需要调整地鼠出现的速度和游戏的其他方面。

全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<!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>
<style>
* {
box-sizing: border-box;
}

h1 {
text-align: center;
line-height: 30px;
}

.bigBox {
width: 60%;
height: 400px;
margin: 20px auto;
background-color: #cbbb3e;
}

.wam-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
height: 260px;
}

.wam-hole {
position: relative;
width: 100px;
height: 100px;
margin: 0 20px;
background-color: #f5732d;
}

.wam-mole {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* 地鼠 */
background-image: url("https://pic.52112.com/180516/EPS180516_57/9jagBhddHW_small.jpg");
background-size: 100% 100%;
display: none;
}

.wam-mole--up {
display: block;
}

.wam-score {
font-size: 2rem;
text-align: center;
}

.wam-message {
font-size: 1rem;
text-align: center;
margin-top: 20px;
cursor: pointer;
}
</style>
</head>

<body>
<h1>打地鼠</h1>
<div class="bigBox">
<div class="wam-container">
<div class="wam-hole">
<div class="wam-mole"></div>
</div>
<div class="wam-hole">
<div class="wam-mole"></div>
</div>
<div class="wam-hole">
<div class="wam-mole"></div>
</div>
<div class="wam-hole">
<div class="wam-mole"></div>
</div>
<div class="wam-hole">
<div class="wam-mole"></div>
</div>
</div>
<div class="wam-score">分数: 0</div>
<div class="wam-message">准备好了吗?点击我开始</div>
</div>

<script>
const container = document.querySelector(".wam-container");
const scoreBoard = document.querySelector(".wam-score");
const message = document.querySelector(".wam-message");
const moles = Array.from(container.querySelectorAll(".wam-hole"));

let lastHole;
let score = 0;
let isPlaying = false;
let timeUp = false;

// 随机时间生成地鼠
function popUpMole() {
if (timeUp) return;
const time = Math.random() * (1500 - 500) + 500;
const hole = randomHole(moles);
hole.querySelector("div").classList.add("wam-mole--up");
setTimeout(() => {
hole.querySelector("div").classList.remove("wam-mole--up");
if (!timeUp) popUpMole();
}, time);
}

// 随机选择一个地鼠洞
function randomHole(holes) {
const idx = Math.floor(Math.random() * holes.length);
const hole = holes[idx];
if (hole === lastHole) return randomHole(holes);
lastHole = hole;
return hole;
}

// 点击地鼠
function whackMole(e) {
if (!e.isTrusted) return; // 防止作弊
if (!isPlaying) return;
if (!e.target.matches(".wam-mole")) return;
score++;
scoreBoard.textContent = `分数: ${score}`;
e.target.parentNode
.querySelector("div")
.classList.remove("wam-mole--up");
}
// 开始游戏
function startGame() {
score = 0;
scoreBoard.textContent = "分数: 0";
isPlaying = true;
timeUp = false;
message.textContent = "";
popUpMole();
setTimeout(() => {
isPlaying = false;
timeUp = true;
message.textContent = `一分钟您的得分是: ${score};点我再来一次!`;
}, 60000);
}

// 初始化地鼠洞
moles.forEach((mole) => mole.addEventListener("click", whackMole));
document
.querySelector(".wam-message")
.addEventListener("click", startGame);
</script>
</body>
</html>

结语

恭喜你,现在你已经创建了一个基本的打地鼠游戏!这个游戏可以作为一个起点,你可以添加计分系统、动画效果、音效等来提升游戏体验。记得保存你的代码,并在浏览器中打开HTML文件来查看游戏效果。祝你编程愉快!

感谢你的访问,期待与你在技术的道路上相遇!👋🌟🚀

在Vue中处理接口返回的二进制图片数据

[TOC]

在Vue中处理接口返回的二进制图片数据

摘要

在现代Web开发中,前端应用经常需要从后端接口获取图片数据。有时,这些图片数据以二进制格式返回,而不是常见的Base64编码。本文将指导你如何在Vue应用中处理这类二进制图片数据,并将其正确地显示在页面上。

1. 问题分析

1.1 环境检查

确保你的Vue项目能够正常发起网络请求,并且后端接口的跨域设置是正确的。如果需要,可以参考相关文档配置axios以适应跨域请求。

1.2 问题描述

当后端接口返回的响应类型为application/octet-streamimage/png等二进制格式时,我们需要特殊处理这些数据。

Snipaste_2024-03-08_21-54-44

2. 接口对接

2.1 添加接口返回头

在axios请求中,我们需要指定responseTypearraybuffer,以便接收二进制数据。

1
2
3
4
5
6
// 使用axios获取二进制数据
request.get("/commonEntity/generateImageCode", {
responseType: "arraybuffer",
}).then((res) => {
console.log(res.data); // 二进制数据
});

2.2 请求数据示例

接口返回的二进制数据通常是一个ArrayBuffer对象。

image-20240308220257299

2.3 使用bufferUrlbtoa函数

为了将二进制数据转换为Base64编码,我们可以使用btoa函数。然后,我们可以将Base64编码的字符串作为图片的src属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import request from "@/request";
import { onMounted, ref } from "vue";

const imgSrc = ref('');

onMounted(() => {
request.get("/commonEntity/generateImageCode", {
responseType: "arraybuffer",
}).then((res) => {
// 将ArrayBuffer转换为Base64编码的字符串
const buffer = new Uint8Array(res);
const base64String = btoa(
buffer.reduce((data, byte) => data + String.fromCharCode(byte), "")
);
imgSrc.value = "data:image/png;base64," + base64String;
});
});

3. 全部代码

以下是一个完整的Vue组件示例,展示了如何获取二进制图片数据并将其显示在页面上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
<img :src="imgSrc" alt="二进制图片">
</template>

<script setup lang="ts">
import request from "@/request";
import { onMounted, ref } from "vue";

// 定义响应式引用imgSrc
const imgSrc = ref('');

// 在组件挂载后发起请求
onMounted(() => {
// 发起请求并处理二进制数据
request.get("/commonEntity/generateImageCode", {
responseType: "arraybuffer",
}).then((res) => {
// 将二进制数据转换为Base64编码
const buffer = new Uint8Array(res);
const base64String = btoa(
buffer.reduce((data, byte) => data + String.fromCharCode(byte), "")
);
// 更新imgSrc的值为Base64编码的图片URL
imgSrc.value = "data:image/png;base64," + base64String;
});
});
</script>

打造经典游戏:HTML5与CSS3实现俄罗斯方块

打造经典游戏:HTML5与CSS3实现俄罗斯方块

摘要

俄罗斯方块是一款经典的电子游戏,它不仅考验玩家的反应速度,还能锻炼逻辑思维能力。本文将指导你如何使用HTML5、CSS3和JavaScript来创建一个简单的俄罗斯方块游戏。我们将从游戏的基本结构开始,逐步构建游戏逻辑,并在最后提供一个完整的代码示例。

1.体验地址

PC端体验地址:洛可可白⚡️俄罗斯方块

(暂时只支持键盘输入操作)

2. 创建游戏界面

首先,我们需要创建一个HTML页面,用于展示游戏的界面。这包括游戏板、得分显示以及游戏控制区域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ... 其他头部代码 ... -->
<style>
/* ... 样式代码 ... */
</style>
</head>
<body>
<h2>俄罗斯方块</h2>
<div id="tetris">
<div id="game-board"></div>
<div id="score">Score: <span id="score-value">0</span></div>
</div>
<!-- ... 脚本代码 ... -->
</body>
</html>

3. 初始化游戏

在JavaScript中,我们首先初始化游戏状态,包括游戏板、得分、当前形状等。我们还需要创建一个函数来生成随机的形状。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ... 其他代码 ...

function createShape() {
// ... 生成随机形状的代码 ...
}

// 初始化游戏状态
const boardGrid = initializeBoard();
let score = 0;
let currentShape = createShape();
let currentRow = 0;
let currentCol = Math.floor(cols / 2) - Math.floor(currentShape[0].length / 2);

// ... 其他代码 ...

4. 绘制游戏板

我们需要编写函数来绘制游戏板和当前形状。这些函数将在游戏开始时和每次形状移动时调用。

1
2
3
4
5
6
7
8
9
10
11
// ... 其他代码 ...

function drawBoard() {
// ... 绘制游戏板的代码 ...
}

function drawCurrentShape() {
// ... 绘制当前形状的代码 ...
}

// ... 其他代码 ...

5. 游戏逻辑

游戏的核心逻辑包括移动形状、检查碰撞、合并形状、清除行和更新得分。我们还需要处理键盘事件,以便玩家可以控制形状的移动和旋转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ... 其他代码 ...

function checkCollision() {
// ... 检查碰撞的代码 ...
}

function mergeShape() {
// ... 合并形状的代码 ...
}

function clearRows() {
// ... 清除行的代码 ...
}

function updateScore() {
// ... 更新得分的代码 ...
}

// ... 其他代码 ...

6. 开始游戏

最后,我们设置一个定时器来自动下落形状,并添加键盘事件监听器来处理玩家的输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ... 其他代码 ...

function startGame() {
// ... 初始化游戏的代码 ...
setInterval(() => {
moveDown();
drawBoard();
drawCurrentShape();
}, 500);
document.addEventListener("keydown", handleKeyPress);
}

startGame();

// ... 其他代码 ...

7.全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
<!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>
<style>
h2 {
font-size: 19px;
text-align: center;
}

#tetris {
width: 240px;
margin: 0 auto;
background-color: #d5d5d5;
border-radius: 10px;
padding: 25px;
}

#game-board {
width: 200px;
height: 400px;
border: 4px solid #4b6014;
position: relative;
border-radius: 10px;
background-color: #f4f126;
margin: 0 auto;
}

#score {
text-align: center;
margin-top: 10px;
}

.block {
width: 20px;
height: 20px;
position: absolute;
background-color: #000;
border: 1px solid #3a3a3a;
box-sizing: border-box;
}
</style>
</head>

<body>
<h2>俄罗斯方块</h2>
<div id="tetris">
<div id="game-board"></div>
<div id="score">Score: <span id="score-value">0</span></div>
</div>
</body>

<script>
document.addEventListener("DOMContentLoaded", () => {
const board = document.getElementById("game-board");
const scoreValue = document.getElementById("score-value");
const blockSize = 20;
const rows = 20;
const cols = 10;
let score = 0;
let boardGrid = Array.from(Array(rows), () => new Array(cols).fill(0));
let currentShape;
let currentRow;
let currentCol;

function createShape() {
const shapes = [
[[1, 1, 1, 1]],
[
[1, 1],
[1, 1],
],
[
[1, 1, 0],
[0, 1, 1],
],
[
[0, 1, 1],
[1, 1, 0],
],
[
[1, 1, 1],
[0, 1, 0],
],
[
[1, 1, 1],
[1, 0, 0],
],
[
[1, 1, 1],
[0, 0, 1],
],
];
const randomIndex = Math.floor(Math.random() * shapes.length);
const shape = shapes[randomIndex];
currentShape = shape;
currentRow = 0;
currentCol = Math.floor(cols / 2) - Math.floor(shape[0].length / 2);
}

function drawBoard() {
board.innerHTML = "";
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
if (boardGrid[row][col]) {
const block = document.createElement("div");
block.className = "block";
block.style.top = row * blockSize + "px";
block.style.left = col * blockSize + "px";
board.appendChild(block);
}
}
}
}

function drawCurrentShape() {
for (let row = 0; row < currentShape.length; row++) {
for (let col = 0; col < currentShape[row].length; col++) {
if (currentShape[row][col]) {
const block = document.createElement("div");
block.className = "block";
block.style.top = (currentRow + row) * blockSize + "px";
block.style.left = (currentCol + col) * blockSize + "px";
board.appendChild(block);
}
}
}
}

function checkCollision() {
for (let row = 0; row < currentShape.length; row++) {
for (let col = 0; col < currentShape[row].length; col++) {
if (currentShape[row][col]) {
const newRow = currentRow + row;
const newCol = currentCol + col;
if (
newRow >= rows ||
newCol < 0 ||
newCol >= cols ||
boardGrid[newRow][newCol]
) {
return true;
}
}
}
}
return false;
}

function mergeShape() {
for (let row = 0; row < currentShape.length; row++) {
for (let col = 0; col < currentShape[row].length; col++) {
if (currentShape[row][col]) {
const newRow = currentRow + row;
const newCol = currentCol + col;
boardGrid[newRow][newCol] = 1;
}
}
}
}

function clearRows() {
for (let row = rows - 1; row >= 0; row--) {
if (boardGrid[row].every((cell) => cell)) {
boardGrid.splice(row, 1);
boardGrid.unshift(new Array(cols).fill(0));
score++;
}
}
}

function updateScore() {
scoreValue.textContent = score;
}

function moveDown() {
currentRow++;
if (checkCollision()) {
currentRow--;
mergeShape();
clearRows();
updateScore();
createShape();
if (checkCollision()) {
gameOver();
}
}
}

function moveLeft() {
currentCol--;
if (checkCollision()) {
currentCol++;
}
}

function moveRight() {
currentCol++;
if (checkCollision()) {
currentCol--;
}
}

function rotateShape() {
const rotatedShape = currentShape[0].map((_, colIndex) =>
currentShape.map((row) => row[colIndex]).reverse()
);
const prevShape = currentShape;
currentShape = rotatedShape;
if (checkCollision()) {
currentShape = prevShape;
}
}

function gameOver() {
alert("Game Over");
resetGame();
}

function resetGame() {
score = 0;
boardGrid = Array.from(Array(rows), () => new Array(cols).fill(0));
updateScore();
createShape();
}

function handleKeyPress(event) {
switch (event.key) {
case "ArrowDown":
moveDown();
break;
case "ArrowLeft":
moveLeft();
break;
case "ArrowRight":
moveRight();
break;
case "ArrowUp":
rotateShape();
break;
}
drawBoard();
drawCurrentShape();
}

function startGame() {
createShape();
setInterval(() => {
moveDown();
drawBoard();
drawCurrentShape();
}, 500);
document.addEventListener("keydown", handleKeyPress);
}

startGame();
});
</script>
</html>

🎉 结语

通过本文的教程,你已经学会了如何使用HTML5、CSS3和JavaScript来创建一个基本的俄罗斯方块游戏。这个项目不仅能够帮助你巩固前端开发的技能,还能让你对游戏开发有一个初步的了解。你可以在此基础上添加更多功能,比如增加难度级别、添加音效或者实现多人游戏模式,来提升游戏体验。

感谢你的访问,期待与你在技术的道路上相遇!👋🌟🚀

入门指南:使用uni-app构建跨平台应用

入门指南:使用uni-app构建跨平台应用

🌟 前言

欢迎来到我的小天地,这里是我记录技术点滴、分享学习心得的地方。📚

🛠️ 技能清单

  • 编程语言:Java、C、C++、Python、Go
  • 前端技术:Jquery、Vue.js、React、uni-app、Echarts
  • UI设计: Element-ui、Antd、Color-ui
  • 后端技术:Spring Boot、Mybatis-plus、Swagger
  • 移动开发:Android
  • 操作系统:Windows、Linux
  • 开发框架:RuoYi、微信小程序
  • 开发工具:VSCode、IDEA、Eclipse、WebStorm、HbuildX、Navicat、Xshell、Android Studio、Postman、GoLand
  • 数据库技术:MySQL、Redis、SQL Server
  • 版本控制:Git

uni-app是一个使用Vue.js开发所有前端应用的框架,可以发布到iOS、Android、Web(包括PC和移动端浏览器)、以及各种小程序(微信/支付宝/百度/字节跳动/QQ/钉钉等)和快应用等多个平台。本教程将带你快速了解uni-app的基本使用。

环境搭建

安装HBuilderX

首先,你需要下载并安装HBuilderX,这是开发uni-app的官方IDE。
HBuildX

创建项目

打开HBuilderX,选择“文件” > “新建” > “项目”,然后选择uni-app模板,填写项目名称和选择项目存储位置,点击“创建”。
“文件” > “新建” > “项目”
创建项目

项目结构

uni-app的项目结构与传统的Vue项目类似,主要包含以下几个部分:
项目结构

  • pages:存放页面文件的目录。
  • static:存放静态资源,如图片、样式文件等。
  • components:存放自定义组件的目录。
  • main.js:应用的入口文件,用于初始化Vue实例。
  • App.vue:应用的根组件。
  • pages.json:页面的配置文件和全局样式配置(如app头部背景色,标题)。
  • manifest.json:应用的配置文件,用于配置应用名称、图标等信息。

开发页面

pages目录下创建一个新的.vue文件,例如index.vue。你可以使用Vue的语法来编写页面内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<view class="container">
<text>欢迎来到uni-app世界!</text>
</view>
</template>

<script>
export default {
data() {
return {
// 页面的初始数据
};
},
methods: {
// 页面的方法
}
};
</script>

<style>
/* 页面的样式 */
.container {
padding: 20px;
text-align: center;
}
</style>

开发页面

运行项目

在HBuilderX中,选择你想要运行的平台(例如Edge浏览器),然后点击工具栏上的“运行”按钮,即可在模拟器或真机上查看效果。
运行项目

组件化开发

uni-app支持Vue组件化开发,你可以创建自定义组件来复用代码。在components目录下创建组件文件,然后在页面中引入使用。

创建组件

components目录下创建一个名为my-component.vue的文件。
创建组件

1
2
3
4
5
6
7
8
9
10
11
<template>
<view>
<text>这是一个自定义组件</text>
</view>
</template>

<script>
export default {
// 组件选项
};
</script>

使用组件

在页面中引入并注册组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<view>
<my-component></my-component>
</view>
</template>

<script>
import MyComponent from '@/components/my-component.vue';

export default {
components: {
MyComponent
}
};
</script>

使用组件

条件编译

uni-app支持条件编译,允许你根据不同的平台编写特定的代码。

1
2
3
4
5
6
7
// #ifdef H5
console.log('这是H5平台特有的代码');
// #endif

// #ifdef MP-WEIXIN
console.log('这是微信小程序平台特有的代码');
// #endif

调试与优化

uni-app提供了丰富的调试工具,包括控制台日志、性能分析等。在开发过程中,合理利用这些工具可以帮助你优化应用性能。


📌 联系方式

如果您对我们的项目感兴趣,或者有任何技术问题想要探讨,欢迎通过以下方式与我联系。我非常期待与您交流,共同学习,共同进步!


🎉 结语

感谢你的访问,如果你对我的技术文章或项目感兴趣,欢迎通过以上方式与我联系。让我们一起在技术的道路上不断前行!🚀


Vue跳转页面传递参数

Vue跳转页面传递参数

[!NOTE]

需求:从搜索页跳到详情页,传递搜索参数到详情页,详情页调用API说去数据,渲染到页面。

路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { createRouter, createWebHistory } from "vue-router";

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "layout",
component: () => import("../LayoutView.vue"),
redirect: "/recommend",
children: [
{
path: "/recommend",
name: "recommend",
component: () => import("../views/RecommendView.vue"),
},
{
path: "/detail",
name: "detail",
component: () => import("../views/DetailView.vue"),
},
],
},
],
});

export default router;

页面一

主要代码

1
2
3
4
5
6
7
8
9
10
11
12
import {ref} from "vue";

const search = ref("");
import {useRouter, useRoute} from 'vue-router'
const router = useRouter()
function handleSearch() {
const data = search.value;
router.push({
path: "/detail",
query: {data}
})
}

页面二

主要代码

1
2
3
4
5
6
7
8
9
10
11
12
import {onMounted} from "vue";
import {useRouter, useRoute} from 'vue-router'
import api from "@/api";

const route = useRoute()
const search = route.query.data;
onMounted(() => {
const searchVal = JSON.stringify(search);
api.home.search(searchVal).then((rs: any) => {
console.log(rs.data.result.songs);
})
})
  • Copyrights © 2022-2024 何福海
  • 访问人数: | 浏览次数:

请我喝杯奶茶吧~

支付宝
微信