| 博客班級 | https://edu.cnblogs.com/campus/zjcsxy/SE2020 |
|---|---|
| 作業要求 | https://edu.cnblogs.com/campus/zjcsxy/SE2020/homework/11334 |
| 作業目標 | 編寫一個小程序,熟悉git代碼管理流程,將源代碼上傳到到github,在博客園班級中寫一篇相應的博文 |
| 作業源代碼 | https://github.com/Cadelle1/demo.git |
| 班級 | 計算機1803 |
| 姓名 | 劉淵琦 |
| 學號 | 31801125 |
介紹:在n×n階的方盤上有n×n-1個滑塊,移動滑塊使得滑塊從左到右從上到下依次排列
成功展示GIF:
| 游戲界面 | 新聞界面 |
|---|---|
![]() |
![]() |
手機上選擇難度界面GIF:

一、結構目錄
小程序包含一個描述整體程序的 app 和多個描述各自頁面的 page。
一個小程序主體部分由三個文件組成,必須放在項目的根目錄
| 文件 | 必需 | 作用 |
|---|---|---|
| app.js | 是 | 小程序邏輯 |
| app.json | 是 | 小程序公共配置 |
| app.wxss | 否 | 小程序公共樣式表 |
一個小程序頁面由四個文件組成,分別是:
| 文件類型 | 必需 | 作用 |
|---|---|---|
| js | 是 | 頁面邏輯 |
| wxml | 是 | 頁面結構 |
| json | 否 | 頁面配置 |
| wxss | 否 | 頁面樣式表 |
二、小程序解析:
1)結構部分

audio 存放游戲點擊音效
images 存放圖片資源和圖標
index 游戲頁面
news 新聞頁面
out 鏈接文件夾
部分用到的組件、API:
定時器
setInterval(function callback, number delay, any rest)
設定一個定時器。按照指定的周期(以毫秒計)來執行注冊的回調函數
clearInterval(number intervalID)
取消由 setInterval 設置的定時器。
調試 console
console.debug()向調試面板中打印 debug 日志
console.log()向調試面板中打印 log 日志
console.info()向調試面板中打印 info 日志
console.warn()向調試面板中打印 warn 日志
console.error()向調試面板中打印 error 日志 等
播放音頻
wx.createInnerAudioContext();
創建內部 audio 上下文InnerAudioContext對象
輪播組件 swiper
滑塊視圖容器。其中只可放置
swiper-item組件
導航欄 自定義 tabBar
自定義 tabBar 可以讓開發者更加靈活地設置 tabBar 樣式
2)功能邏輯部分
[1]. 如何移動?
- 關於Es6 解構賦值:
解構賦值是對賦值運算符的擴展,是一種針對數組或者對象進行模式匹配,然后對其中的變量進行賦值,在代碼書寫上簡潔且易讀,語義更加清晰明了;也方便了復雜對象中數據字段獲取。
如index.js中:[numData[i], numData[x[1]], numData[x[2]]] = [numData[x[2]],numData[i],numData[x[1]],];(滑塊的移動) - 滑塊能否移動的判斷,超出邊界時的判斷
- 當前想要移動的位置處在最左邊和最右邊時的情況,禁止移動
[2]. 數字華容道有解的條件
- 若格子列數為奇數,則逆序數必須為偶數
- 若格子列數為偶數,且逆序數為偶數,則當前空格所在行數與初始空格所在行數的差為偶數
- 若格子列數為偶數,且逆序數為奇數,則當前空格所在行數與初始空格所在行數的差為奇數
三、對小程序進行的修改:
1. 對小程序的界面風格進行了改進
| 原來 | 現在 |
|---|---|
![]() |
![]() |
2. 添加了歷史最快成績,並按照難度分別存儲
用了一維數組來存儲歷史最快成績,默認為60分鍾(游戲總時長),每次計時器停止時,用本次使用時間與歷史上同難度最快時間進行比較,若時長變短則更新記錄。
部分js
if(this.data.fm[this.data.nowDifficulty-3]*60+this.data.fs[this.data.nowDifficulty-3]>=parseInt(m) *60+parseInt(s)){
fm_val=m;
fs_val=s;
}else{
fm_val=fm[this.data.fm[this.data.nowDifficulty-3]]
fs_val=fs[this.data.fs[this.data.nowDifficulty-3]]
}
this.setData({
[fm_path]:fm_val,
[fs_path]:fs_val,
});
3. 添加了一次移動多個滑塊的功能
原小程序只是點擊空白快相鄰的滑塊,滑塊與空白處位置互換,現能夠點擊與空白塊非相鄰且同行或者同列的滑塊實現多個滑塊一起移動,即實現了一次滑動多塊滑塊(能夠有效滑動),並且計步加1。
難點:
1.根據不同的難度以及空格的位置計算出可以實現移動的方塊的位置:比如,難度為3,可一次性移動的格數為1、2格,難度為5,可一次性移動的個數為1、2、3、4格;若難度為3,移動的個數為2時,那空格處向上向下向左向右距離兩格的方塊(若有方塊)是可以被移動的。
2.僅同行同列的滑塊能夠同時移動,限制其他時候點擊滑塊造成滑塊位置交換混亂。同一列滑塊的位置值對nowDifficulty 取余數應該全部相等,同一行滑塊的位置值對nowDifficulty做整除
Math.floor()向下取整應該全部相等

部分js
// 移動算法
isPass: false,
goMove(e) {
// 通關 或者 沒開始游戲 就不能移動
if (this.isPass || !this.isStart) return;
let index = e.currentTarget.dataset.index,
nowDifficulty = this.data.nowDifficulty,
numData = this.data.numData,
step = this.data.step;
let flag = 0; // flag 表示有 flag 個格子即將被移動
let x = [0, 0, 0, 0, 0, 0, 0]; // x[i] 表示第i個會被移動的格子所在的index值
for (let i in numData) {
if (index == i) {
x = [0, 0, 0, 0, 0, 0, 0]; // 重寫x數組為全零,防止被以前的數據污染
for (let i = 1; i < nowDifficulty; i++) {
// i 為可能被移動的格子的數量,空白格子不計數
// 外循環用於遍歷 i 相對於 nowDifficulty 的可能性
for (let j = 1; j <= i; j++) {
// 內循環用來遍歷 j 相對於 i 的可能性
if (isGoal(numData, index + j)) {
//如果判定index + j處為空格,那么,可以生成x[j] 並終止循環
// flag即會被移動的格子的數量,就是j
flag = j;
for (let k = 1; k <= j; k++) {
x[k] = index + k;
}
break;
} else if (isGoal(numData, index - j)) {
flag = j;
for (let k = 1; k <= j; k++) {
x[k] = index - k;
}
break;
} else if (isGoal(numData, index + j * nowDifficulty)) {
flag = j;
for (let k = 1; k <= j; k++) {
x[k] = index + k * nowDifficulty;
}
break;
} else if (isGoal(numData, index - j * nowDifficulty)) {
flag = j;
for (let k = 1; k <= j; k++) {
x[k] = index - k * nowDifficulty;
}
break;
}
}
//如果flag已經不是零了,那么說明已經得到了格子移動的數組 x[],終止循環
if (flag != 0) break;
}
// 根據 flag 和 x[] 進行實際的移動
if (flag == 0) {
console.log("未觸發移動");
return;
} else {
// 檢查移動是否是在 同一行/同一列
console.log("\n\n", index, "\n");
let mark; // 用於保存 校驗特征 的變量
let check_column = true; // 用於保存 校驗結果 的變量
mark = index % nowDifficulty; // 首先檢查 列
// 如果 會被移動的格子x[] 和 當前觸摸的格子index 位於同一列,位置值 對 nowDifficulty 取余數 應該全部相等
for (let i = 1; i < nowDifficulty; i++) {
if (x[i] == 0) continue;
console.log(x[i], "%", nowDifficulty, ":", x[i] % nowDifficulty);
check_column = check_column && x[i] % nowDifficulty == mark;
}
mark = Math.floor(index / nowDifficulty); // 其次檢查 行
// 如果 會被移動的格子x[] 和 當前觸摸的格子index 位於同一行,位置值 對 nowDifficulty 做整除 應該全部相等,Math.floor() 來向下取整,以得到整除的效果
let check_row = true;
for (let i = 1; i < nowDifficulty; i++) {
if (x[i] == 0) continue;
console.log(
x[i],
"/",
nowDifficulty,
":",
Math.floor(x[i] / nowDifficulty)
);
check_row = check_row && Math.floor(x[i] / nowDifficulty) == mark;
}
console.log(
"check_column is ",
check_column,
"check_row is ",
check_row
);
if (!check_column && !check_row) return;
// 移動使用 ES6 解構賦值
switch (flag) {
case 1:
[numData[i], numData[x[1]]] = [numData[x[1]], numData[i]];
break;
case 2:
[numData[i], numData[x[1]], numData[x[2]]] = [numData[x[2]],numData[i],numData[x[1]],];
break;
case 3:
[numData[i], numData[x[1]], numData[x[2]], numData[x[3]]] = [numData[x[3]],numData[i],numData[x[1]],numData[x[2]],
];
break;
case 4:
[numData[i],numData[x[1]],numData[x[2]],numData[x[3]],numData[x[4]],] = [numData[x[4]],numData[i],numData[x[1]],numData[x[2]],numData[x[3]],];
break;
case 5:
[numData[i],numData[x[1]],numData[x[2]],numData[x[3]],numData[x[4]],numData[x[5]],] = [numData[x[5]],numData[i],numData[x[1]],numData[x[2]],numData[x[3]],numData[x[4]],];
break;
case 6:
[numData[i],numData[x[1]],numData[x[2]],numData[x[3]],numData[x[4]],numData[x[5]],numData[x[6]],]=[numData[x[6]],numData[i],numData[x[1]],numData[x[2]],numData[x[3]],numData[x[4]],numData[x[5]],];
break;
default:
break;
}
step += 1;
innerAudioContext.play(); // 播放移動效果的音樂
}
}
}
this.setData({ step, numData });
this.gameOver();
},
4. 底部添加了導航條,顯示游戲界面和新聞界面

| 游戲界面 | 新聞界面 |
|---|---|
![]() |
![]() |
部分json
"tabBar": {
"color": "#F3CC95",
"selectedColor": "#996E4A",
"borderStyle": "black",
"list": [
{
"selectedIconPath": "images/game.png",
"iconPath": "/images/game.png",
"pagePath": "pages/index/index",
"text": "游戲"
},
{
"selectedIconPath": "images/news.png",
"iconPath": "/images/news.png",
"pagePath": "pages/news/news",
"text": "新聞"
}
]
}
5. 添加了輪播動畫以及文本鏈接(沒有配置業務域名)
輪播:

部分wxml
<swiper class="swiper" indicator-dots="true" autoplay="true" interval="5000" duration="1000">
<block wx:for="{{movies}}" wx:for-index="index">
<swiper-item>
<image src="{{item.url}}" class="slide-image" mode="aspectFill"/>
</swiper-item>
</block>
</swiper>
部分js
var app = getApp()
Page({
data: {
movies:[
{url:'/images/l1.jpg'} ,
{url:'/images/l2.jpg'} ,
{url:'/images/l3.jpg'} ,
{url:'/images/l4.jpg'} ,
]
},
onLoad: function () {
} ,
toOut:()=>{
wx.navigateTo({
url: '/pages/out/out',
})
}
})
鏈接:

<!--pages/out.wxml-->
<web-view src="https://www.baidu.com"></web-view>
<!--pages/news.wxml-->
<text class="headtext" bindtap="toOut">最強大腦!8歲女童3.6秒復原數字華容道 </text>
<!--news.js-->
toOut:()=>{
wx.navigateTo({
url: '/pages/out/out',
})
}
原版數字華容道參考博文:https://blog.csdn.net/qq_23375733/article/details/101054053
小程序學習參考文檔:微信開放文檔






