《眼疾手快》项目小结

眼疾手快是国庆期间用小程序开发的一个益智小游戏,游戏的玩法是:每一局都从数字1-9中随机抽出一个数字作为答案,然后把剩下的8个数字顺序打乱,随机采取横或者竖或者九宫格的布局展示在页面上,让用户点击输入缺失的数字,要求输入准确而且时间越快越好。

核心算法的实现

核心算法就是要从1-9中随机抽出一个数字作为答案,并且打乱剩下八个数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
makeQuestion () {
const ansIndex = Math.floor(Math.random() * 9) + 1;
let num = '123456789', curNum = '', question = [], answer = '';
for (let k = 9; k > 0; k--) {
curNum = num.charAt(Math.floor(Math.random() * k));
num = num.replace(curNum, '');
if (k === ansIndex) {
answer = curNum;
curNum = '';
}
question[k - 1] = curNum
}
return {
question: question,
answer: answer
}
}

图片加载遇到了问题

首先这是个很小的教学类游戏,图片资源较少,所有的图片资源都放在本地,当用户下载完小程序包之后都是本地的资源的引用,加上时间紧急我就没有考虑做图片预加载,但是在测试阶段发现还是偶尔有部分图片会长时间没有加载出来,这样问题就有点诡异。

小程序内做图片预加载吧

第一想法就是做图片预加载。在web中我们有Image对象,只需要写一个队列,当所有需要预加载的图片都onload完毕之后返回成功状态就可以了,简单实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
function preloadImg(urls) {
if (!Array.isArray(urls)) return Promise.resolve();
const list = urls.map(link => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = resolve;
img.onerror = reject;
img.src = link;
}
});
return Promise.all(list);
}

但是小程序里面没有Image对象,那就不能这样实现了,但是虽然没有Image对象,但是小程序的Image组件还是有提供binderrorbindload事件,可以绕一下,把需要预加载的图片直接写在一个页面上,然后上面覆盖loading图,当所有图片load事件返回之后再把loading去掉就可以了,但是因为上线时间比较紧就没有加预加载

图片资源转为base64吧

第二个想法就是转base64吧。直接用webpack转吧,在webpack.base.conf.js中找到url-loader把图片的limit弄大一点,100K吧

1
2
3
4
5
6
7
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 102400
}
},

然后看一眼打包之后的代码,发现并没有生效,代码里的图片还是链接,但是控制台明明显示所有图片都emitted了,而且直接输出到了文件夹,好像并没有经过url-loader处理,在issues上搜下发现也有人遇到同样的问题,然后维护者说mpvue-loader处理后直接emitfile 了,没发送给其他图片loader处理,坑~
最终还是手动把部分小的图标转成了base64,然后现在代码里面看起来就一坨坨的

小总结

  1. 具体问题其实并没有定位到,Image加载没有报错
  2. 跟图片大小无关,大小图片偶有无法加载出来的时候
  3. 无论是预加载和base64两个方向应该都是有问题的,毕竟小程序已经把所有本地资源下载好了,不应该是网络问题,而且转base64后解析应该更耗性能了,但是加载空白的问题几乎不再有
  4. 小程序文档在性能优化那里强调目前图片资源的主要性能问题在于大图片和长列表图片上,这两种情况都有可能导致 iOS 客户端内存占用上升,从而触发系统回收小程序页面,但是和这次表现不符合
  5. 后面可以实现一个小程序版的图片预加载

设计要求界面上使用特定字体

因为眼疾手快是一款对外的教育类游戏,设计对界面还是有一定的要求,其中就提到希望界面上固定的文案能够使用圆体,这样整体UI会比较有感觉。第一想法就是上iconfont.cn上转换和下载,但是发现网站上提供的字体有限,并没有圆体,那就只能寻找别的字体源,而且因为中文字体库太庞大,还得做字体提取。字蛛是一个开源的中文字体压缩器,它可以吧html中用到的字进行提取和压缩,用不到的就过滤掉,这样就能把整个ttf字体进行强而有力的压缩,这个项目里面我使用到的字体压缩之后就只有4k。而且小程序不支持本地加载字体文件,只能把字体放在网络上通过链接引用,或者使用wx.loadFontFace来异步加载,因为考虑到异步会有延迟,所以直接就直接把字蛛切割后的ttf文件在transfonter.org上转换成base64引入,这样就实现了设计使用特殊字体的要求。

登录授权逻辑

微信为了保护用户隐私,对wx.getUserInfo做了限制,withCredentials字段必须在wx.login且登录态未过期的时候才生效,而wx.login的有效期为5分钟,该api为静默调用,用户没有任何感知,而且小程序不会再主动调起授权弹窗,授权弹窗必须通过button组件设置open-type="getUserInfo"来调用,这就形成了一个迷之逻辑,wx.login由开发者主动调用而且只有5分钟有效期而且得在getUserInfo之前,getUserInfo目前只能通过button组件让用户主动触发,如果五分钟后用户还没有调用button组件,wx.login的登录态就失效了。最终我的做法就是一进入应用就主动调用login,然后首页的每一个按钮都设置open-type="getUserInfo",然后在回调里再调一次login,这样如果登录态已经失效,用户再次掉起授权的时候就能确保登录态是有效的了

游戏二维码