随机转盘小程序的设计与实现
Posted OurShiningDays
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了随机转盘小程序的设计与实现相关的知识,希望对你有一定的参考价值。
1 要求描述
1.1 程序需求描述
·制作一个转盘小程序,内置多种场景供用户选择。用户可以选择系统内置的场景,也可以自行添加场景。选择场景后,用户可以点击开始按钮,转盘转动并随机生成场景中的某一项。
1.2 其他要求描述
·明确要求:使用PSP记录开发流程所需时间。
·自行要求:该作业为替代反向工程(https://www.cnblogs.com/ourshiningdays/p/14858512.html)作业的替代。反向工程作业的截止日期为9月15日23时59分59秒(以发博客的时间为准)。因此决定对本作业使用相同的截止日期。
2 分析
2.1 基本功能模块分析
根据程序需求描述,程序可以分为如下基本功能模块:
·转盘模块:能够显示各种选项,能够正常进行操作、生成随机结果等。
·场景模块:该模块应细分成系统内置场景模块、用户自定义场景模块。
2.2 技术路线及技术可行性分析
技术路线:微信小程序提供官方API文档,有一套专门的微信小程序API。需要掌握其使用方法。另外,还需使用CSS进行样式控制、javascript进行事件处理、程序功能逻辑处理等。程序暂不需要后端及相关知识。
技术可行性:由于技术路线中涉及的技术栈接触较少,此前未进行过相关实际开发,预计在实现过程中会遇到相关语法/用法的困难。该小程序的程序逻辑清晰,思路较易获得。该小程序实现时存在一些需要处理的细节。结合以上分析,若要在6天内基本完成,开发策略是首先快速学习微信小程序最基础的结构,在语法上“即用即查”、“即学即用”,必要时可能需要参考一些现有demo,优先完成主要核心功能,方可保证在规定时间内的进度。
3 系统实现
3.1 Tab实现
在微信小程序中,可以通过控制app.json中的tabBar项进行Tab的编辑。[1-2]
此处按照要求编写代码即可,故直接使用代码说明:
"tabBar":{
"borderStyle": "white",
"selectedColor": "#4A6141",
"color": "#333",
"backgroundColor": "#FFF",
"position": "bottom",
"list": [
{
"pagePath": "pages/wheel/wheel",
"text": "圆盘",
"iconPath": "images/wheel_unclicked.png",
"selectedIconPath": "images/wheel_clicked.png"
},
{
"pagePath": "pages/scene/scene",
"text": "场景",
"iconPath": "images/scene_unclicked.png",
"selectedIconPath": "images/scene_clicked.png"
}
]
}
3.2 圆盘
圆盘实现是在完成本作业中最困难的部分,该部分在实际开发中首先进行了具体技术栈的查询,得知一种方式是按照本文第二部分的分析,使用CSS进行绘制,另外有使用canvas进行绘制的方法。
接下来进行该功能模块总体思路的建构:
I.使用某种方法绘制圆盘、绘制分割线、绘制转盘开始按钮
II.通过某种方式进行数据的获取,数据应该分为一个索引index和选项名,一个index对应一种选项。
III.随机生成一个[0,length)的整数随机数作为index。
IV.播放转盘动画,转盘转动角度是N*360°+(360°/选项个数*index),其中N为转动圈数。
V.获得该index对应的选项名,提示给用户。
根据该思路尝试编写代码,但多次尝试均未得到正确结果,决定参考一些demo。
由于微信小程序支持模块化编程[1],在网上无意间找到了与示例小程序极为相似的成熟转盘组件[3],但直接引用或直接学习该组件使得该作业失去训练意义,决定不参考该项目,但将该较为成熟的项目在本博客中收录,供有相关需求的用户参考使用,也方便自己在未来有需要时进行学习。本作业不考虑直接使用模块。
经过一段时间的寻找,找到了如附[4]的demo,总体思路与前面自行分析时生成的思路有较高的相似度。
拿到demo首先就是对自己的思路进行对比,发现总体还是基本一致的,但在转盘转动角度上,demo作者设置的角度为
app.runDegs = app.runDegs || 0
app.runDegs = app.runDegs + (360 - app.runDegs % 360) + (360 * runNum - selectionIndex * (360 / app.selectionConfig.selection.length))
表达式第三项基本与上述分析IV相似,但作者采用的是减法,这是因为如果按照之前分析IV的方法,如果在绘制转盘时选项按照顺时针顺序绘制,旋转时应该逆时针旋转才成立。
如图所示,在填选项的时候按照绿色顺时针方向进行,则index如图所示,指针初始指在[0]处,如直接顺时针旋转360/8*1,停留的是[8]处,这是不对的。而微信默认的动画旋转为顺时针,因此作者使用减法的方式是正确的,之前IV分析考虑不周全。
另外,由于转盘转动一次后,转盘已经不是初始角度了,这样在计算的时候必须对当前角度进行处理,或者是像要求中的示例小程序那样重置转盘。找到的demo采用了第一种方法。而在我之前分析的时候没有考虑到这一点问题,是细节疏忽。
该demo中有两种颜色,我期望的是如要求中的示例小程序一样绘制多种颜色,绘制思路也比较简单:
I.事先建立好一个color数组存储颜色。数组容量=最多允许有几种选项。
II.循环绘制扇形。将颜色填充为color[i]。
浏览作者的demo代码,作者也是绘制了扇形,但只指定两种颜色:根据index%2来确定使用哪种颜色。我按照自己的思路进行代码修改,在模拟器上得到了正确的彩色转盘。但是此时我并没有尝试使用真机测试,在我后续使用真机测试的时候,发现填充的扇形会将圆盘和文字全部盖住,这是因为扇形采用canvas进行绘制,而canvas在微信小程序中属于原生组件,无法单独设置z-index,属于最高层级,因此它会直接覆盖住圆盘和文字,并且也没办法应用动画。因此基本论证技术路线的失败。作者另外给出了一种实现方法,但实际上圆盘只有一种颜色,如想得到多种颜色使用canvas进行绘制,就会复现问题。而此时时间已经来不及,只好暂时作罢。
可能实现转盘着色的技术路线:CSS、Canvas2d等其他新组件[5]?
有了单色转盘,便可以进行其他功能的编写,此时已经在该组件中花费了较多的时间。
3.3 场景模块
3.3.1 系统内置场景数据
系统内置场景可以使用如下数据结构表示:
selectionConfig:JSON格式数据数组。存储场景全部信息。
selectionConfig每一项包括:
title:场景标题-String,
chance:是否可以抽奖-Boolean-默认=true,
selection:JSON格式数组。存储所有选项。
selection包括:
index:int类型。该选项的索引。
name:String类型。该选项的名称。
将该数据存储至data/data.js目录中。
3.3.2 用户自定义场景数据
数据结构与内置场景数据应一致。但要存储至微信小程序提供的本地缓存中,使用微信小程序提供的增删改查接口对其进行访问及操作[1-2]。
3.3.3 内置场景数据的显示
实现方法如下:
使用wx:for循环对3.3.1所述数据结构进行引用,将调用数据结构的title标题进行数据绑定,即可完成显示。但需要注意的是,在wxss中写代码进行显示的时候,要添加点击事件。
点击事件使用微信小程序的全局变量进行传递,使用一个全局变量存储当前的index,默认为0。在点击时将该全局变量置为当前index,并切换至转盘页,并刷新。切换并刷新的代码如下[6]:
wx.switchTab({
url: \'../wheel/wheel\',
success: function (e) {
var page = getCurrentPages().pop();
if (page == undefined || page == null) return;
page.onLoad();
page.onReady();
}
})
代码获取了当前页面栈的实例,将当前页弹出,由于程序打开是转盘页,切至场景页时,当前页的上一页是转盘页,即目标页。然后重新执行onLoad()和onReady方法,即可重新刷新数据。
3.3.4 用户自定义场景页面的实现
用户自定义场景页面首先应该做一个顶部tab,切换内置场景和自定义场景。[7]
在自定义场景页面中要显示出所有的信息,和上文所述系统内置的场景方法类似,只不过这里要从缓存里读数据,在data.js中使用userSelection存储,数据结构同上。使用一个固定的获得所有数据的方法,如下:
getAllUserSelection() {
var res = wx.getStorageSync(this.storageKeyName);
if (!res) {
res = require(\'../data/data.js\').userSelection;
this.execSetStorageSync(res);
}
return res;
}
在页面中直接调用该方法即可显示。
然后应该显示一个添加场景的按钮,点击之后应跳转到添加场景的页面中。这里通过顶部tab的index决定是否显示,为0时是预置场景——不显示添加按钮,为1时是用户场景——显示添加按钮,如下:
<view wx:if="{{currentIndex == 1}}" class="addBtn" bindtap="onAddBtn">添加场景</view>
点击该按钮的事件是跳转到一个新的添加场景的页面。
3.3.5 添加页面的实现
该部分流程如下:
I.生成一个存储标题的输入框。
II.生成一个点击添加选项的输入框。添加前校验选项数量。
III.选项输入框中添加删除功能。
IV.保存前校验是否为空。
V.保存至本地缓存数据库。
使用表单实现,保存按钮为submit。
具体事件:
每当点击添加时,如没有超过最大选项数量,添加一个带删除按钮的输入框。
每当有输入事件时,从输入框中获取输入数据保存在数组中。
每当有删除事件时,从数组中删除。
点击保存时,调用isValid函数进行判断,部分isValid函数代码如下。
isValid: function (data) {
if (data.detail.value.sceneName === ("")) {
wx.showToast({
title: \'场景名是必填的!\',
icon: "none"
})
return false;
}
for (var i = 0; i < this.data.array.length; i++) {
if (this.data.inputVal[i] == undefined || this.data.inputVal[i].length == 0) {
wx.showToast({
title: \'您有选项没填写!\',
icon: "none"
})
return false;
}
}
return true;
},
如判断通过,要进行保存,但这里获得的输入数据和上部分描述的数据结构相比有较大差异,这里获得的只是一个保存输入信息的数组。因此要进行转为符合此前的数据结构的格式,然后再进行保存。具体代码如下:
var info = {"title":data.detail.value.sceneName,"chance":"true","selection":this.data.saveVal}
var JSONInfo = JSON.parse(JSON.stringify(info))
var userSelection = dbPost.getAllUserSelection();
userSelection.push(JSONInfo)
wx.setStorageSync(\'userSelection\', userSelection)
wx.showToast({
title: \'添加成功\',
icon:\'success\'
})
wx.navigateBack();
这里有一个功能没实现好:返回后应该将3.3.4所属页面数据进行刷新,目前没有刷新,需要用户手动切换才能看到刚刚添加的数据。
3.4 圆盘页面其他相关功能
要求中的示例小程序还有几个其他的功能。本次作业也对此进行了实现。
3.4.1 标题及副标题
圆盘上方应显示标题,副标题转转盘之前是“???”,转转盘之后显示结果。
实现方法:
直接从数据结构中取得标题进行显示即可。
副标题固定写为“???”,转转盘之后将转到的index对应的selection设置为副标题即可。
3.4.2 生成三种建议场景
要求中的示例小程序这部分是简单的使用内置场景的第1~第3个场景。
本作业中,将这部分进行随机化。
首先要控制转盘页在数据结构中取得三项标题,在注[8]中作者提供了利用wx:if来控制wx:for循环列表数量的方法,但该文章中的index是直接写进代码里的,这里进行一下处理,在js中使用random随机生成一个[0,场景数组长度-3]的整数,该整数存储在变量randomIndex内,然后在wx-if数据绑定处进行对应的修改,代码如下:
<view class="hint">
<text class="hintRandom"> 我们为您随机推荐了以下场景:</text>
<block wx:for="{{selectionInfo}}" wx:for-item="item" wx:for-index="idx"
wx:if="{{idx>=randomIndex&&idx<randomIndex+3}}">
<view class="titleList">
<view bindtap="clicked" id="{{idx}}">
<text class="hintTitle"> {{item.title}}</text>
</view>
</view>
</block>
</view>
这样便可以在用户点击小程序之后,随机生成三个连续的场景。
3.5 转盘背景音乐
在转盘动画进行时、提示用户结果时分别进行音乐的播放,微信小程序中音乐的播放代码可以按照如下实现:
const bgMusic = wx.getBackgroundAudioManager()
bgMusic.title = \'得到结果\';
bgMusic.src = \'音乐网址\';
bgMusic.play();
其中需要注意的是.src不支持播放本地音乐,要设法获取到一个支持外链的音乐网址。
3.6 程序截图
4 PSP记录
PSP表格如下
分类 | 开始时间 | 结束时间 | 中断时间/min(s) | Delta Δ/min(s) | 任务内容/描述 |
Preparing | 9.9 22:01 | 9.9 22:07 | 0 | 6 | 请求本部图书馆《微信小程序开发入门与实践》 |
Preparing | 9.10 15:13 | 9.10 15:17 | 0 | 4 | 取书 |
Reading | 9.10 19:33 | 9.10 19:41 | 0 | 8 | 阅读前面 |
Preparing | 9.10 19:41 | 9.10 20:12 | 1 | 30 | 安装环境、申请账号、创建项目等 |
Reading | 9.10 20:30 | 9.10 21:13 | 1 | 42 | 阅读学习第三章(读+实践) |
Reading | 9.11 00:31 | 9.11 00:58 | 3 | 24 | 阅读学习至p72 |
Reading | 9.11 08:17 | 9.11 08:45 | 5 | 23 | 阅读学习至p82 |
Reading | 9.11 10:41 | 9.11 11:09 | 0 | 28 | 阅读学习至p98 |
Summarizing | 9.11 13:11 | 9.11 13:29 | 0 | 18 | 反思总结目前遇到的问题。 |
Searching | 9.11 13:39 | 9.11 14:32 | 5 | 48 | 搜索转盘绘制所需知识 |
Preparing | 9.11 14:34 | 9.11 14:54 | 0 | 20 | 收集制作图片素材 |
Trying | 9.11 15:00 | 9.11 15:27 | 0 | 27 | 研究绘制转盘的方法 |
Trying | 9.11 15:43 | 9.11 17:08 | 9 | 76 | 研究绘制转盘的方法 |
Trying | 9.11 18:23 | 9.11 19:27 | 6 | 58 | 研究绘制转盘的方法 |
Coding | 9.11 22:00 | 9.11 22:31 | 1 | 30 | 学习、编写、修改转盘部分代码 |
Coding | 9.12 13:32 | 9.12 16:03 | 31 | 120 | 学习、编写、修改转盘部分代码 |
Coding | 9.12 21:29 | 9.12 21:48 | 0 | 19 | 学习并编写Tab部分(完成) |
Coding | 9.12 22:33 | 9.12 23:19 | 6 | 40 | 把数据单独拿出放到data.js |
Coding | 9.13 14:00 | 9.13 15:40 | 3 | 97 | 显示场景列表(CSS未写);完成点击内置场景时数据的切换 |
Coding | 9.13 15:53 | 9.13 16:25 | 1 | 31 | 扩充内置场景数据至10种 |
Coding | 9.13 21:31 | 9.13 23:07 | 9 | 87 | 完成主页随机生成三个连续场景及其点击后数据的切换功能;补充主页及场景页CSS样式; |
Coding | 9.14 12:41 | 9.14 13:20 | 5 | 34 | 场景页切换tab-CSS |
Coding | 9.14 14:00 | 9.14 14:11 | 1 | 10 | 场景页切换tab-CSS |
Coding | 9.14 19:04 | 9.14 19:19 | 1 | 14 | 点击切换事件代码结构 |
Reading | 9.14 19:12 | 9.14 19:30 | 0 | 18 | 读书寻找关于缓存数据库内容 |
Coding | 9.14 19:31 | 9.14 20:43 | 3 | 69 | 仅在用户自定义场景显示添加场景按钮的逻辑判断,缓存相关代码的编写(未实现) |
Coding | 9.14 22:33 | 9.14 23:47 | 3 | 71 | 添加按钮的点击跳转事件 |
Coding | 9.15 10:10 | 9.15 11:38 | 2 | 86 | 添加场景页面的编写 |
Coding | 9.15 11:51 | 9.15 12:29 | 2 | 36 | 添加场景页面的编写 |
Coding | 9.15 16:13 | 9.15 19:04 | 4 | 167 | 添加场景页面的编写、保存时对用户输入的校验、保存后数据的存储及读取、转盘对系统内置场景及用户场景两种不同情况的数据引用 |
Coding | 9.15 19:40 | 9.15 20:08 | 0 | 28 | 尝试添加音乐播放 |
Blogging | 9.15 20:19 | 9.15 23:35 | 2 | 194 | 撰写博客 |
总计 | 1563 |
PSP饼图如下
5 反思与总结
5.1 记录缺陷
根据导师及组内师兄对其他同学作业的修改意见,得知PSP中断时间>5分钟时应分开记录,>30分钟时应分两次记录,我在收到该意见之前的PSP记录不符合这一要求,今后记录时需要加以改正。
5.2 能力缺陷
该程序编写时所涉及功能的基本思路基本可以知晓,并有一定的正确度,但在具体实现时由于程序完成时间的限制和相关技术栈的不熟练,仍然遇到较多的困难,即使花费了较多的时间,仍然在程序中的一部分参考了他人的demo代码、部分功能未完善。
5.3 参考时缺陷
上文提到过使用demo遇到的真机无法正确运行的问题。虽然通过分析,知道了问题的原因,但若参考demo前首先测试一下demo能否在模拟器和真机运行成功,将会节省下一些时间,避免时间的浪费。
5.4 本程序目前存在的不足
较要求示例小程序相比,本作业目前存在以下不足:
- 添加场景后,数据不会自动刷新。
- 转盘只有一种颜色。
- 点击转盘时没有遮罩。
- 用户自定义场景中尚未加入编辑和删除的功能。
5.5 本程序目前的额外功能
较要求示例小程序相比,本作业目前存在以下额外功能:
+ 转盘页面的提示场景可以随机生成。
5.6 完成本作业的收获
+ 初步了解了微信小程序的基本结构,了解了一些组件的使用方法。
+ PSP在实际小项目中的第一次应用。
+ 对JS CSS相关知识的一次学习与实践。
+ 程序中有很多细节需要考虑。加强了考虑的全面性。
+ 以上反思、导师和师兄提出的意见均为收获。
+ 降低了对未知技术的恐惧感。
6 引用与致谢
感谢导师及师兄/师姐的阅读及建议。
感谢如下内容对本作业的帮助。
1、书籍:雷磊编著,《微信小程序开发入门与实践》,清华大学出版社
2、小程序官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/
3、微信小程序 自定义组件之《转盘》:https://blog.csdn.net/qq_23375733/article/details/81274955
4、本作业中参考的转盘示例:https://www.wxapp-union.com/forum.php?mod=viewthread&tid=420&highlight=canvas
5、小程序 canvas 2d 新接口 绘制带小程序码的海报图https://www.cnblogs.com/alpiny/p/12574017.html
6、使用 navigator 标签跳转 TAB 页面报错:wx.switchTab: url 不支持 queryString:https://www.ijiaoyu.org/info/4.html
7、微信小程序实现tab切换和for循环嵌套
https://blog.csdn.net/qq_42543264/article/details/106644155
8、小程序wx:for循环列表数量的限制:https://blog.csdn.net/qq_38194393/article/details/88024809
9、小程序开发中var that = this的用法说明:
https://blog.csdn.net/qq_32534441/article/details/105607334
10、前端相关知识速查:https://www.w3school.com.cn/
11、小程序文字下划线的用法:https://my.oschina.net/u/2494575/blog/2254168
12、微信小程序中e.target与e.currentTarget区别详解https://blog.csdn.net/shadow_zed/article/details/104398191
13、微信小程序中button中字体默认加粗解决方案
https://blog.csdn.net/sunlit_6/article/details/116974787
以上是关于随机转盘小程序的设计与实现的主要内容,如果未能解决你的问题,请参考以下文章