博客班级 | 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
小程序学习参考文档:微信开放文档