尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微信小程序摇色子实战:随机数、动画与setData性能优化

微信小程序摇色子实战:随机数、动画与setData性能优化 简介一款微信小程序项目实例“摇色子”围绕小游戏开发展开面向正在学习小程序开发或希望快速上手小游戏项目的读者适合作为课堂练习与个人练手素材。资源包共22个文件压缩后仅22KB包含6个JSON配置、5个WXSS样式、5个JS逻辑、4个WXML页面及2张PNG图片覆盖了从全局配置、页面结构到样式与交互控制的完整项目骨架。已有1569人浏览学习具有一定参考热度。通过该实例可以直观了解小程序事件监听、随机数生成、动画过渡与基础数据管理方式同时掌握页面导航、全局配置和组件拆分等实用技巧资源中已包含可直接运行的微信开发者工具项目配置导入后即可预览效果。项目体量小巧、目录清晰便于逐文件研读或二次改造适合用较短时间完成一次完整的小程序开发实践。1. 摇色子小程序里最值得手写的“小游戏”把“摇色子”当成一个微信小程序项目实例来做很多人第一反应是“这有什么好写的”。但恰恰是这种看上去只有一两个交互的小项目能把小程序从页面渲染、事件绑定、setData 性能、API 权限到真机调试的整条链路全部走一遍。你不需要后端不需要云开发甚至不需要任何第三方 UI 库就能在一个晚上产出一个能分享到群里的完整作品。这个标题背后真正的价值是让你用最小的成本理解“微信小程序游戏开发”和普通 H5 页面的差异WXML 的模板语法、双线程渲染模型、setData 的异步提交这些不是看文档能记住的而是要在摇色子这种反复触发随机数、反复变更视图的例子里踩过坑才记得牢。适合谁看正在做微信小程序毕业设计的学生、想从“会写页面”过渡到“会写交互”的前端开发者以及用 uniapp 开发微信小程序但一直不清楚原生 API 边界的同学。2. 摇色子的核心机制随机数、动画与事件循环2.1 为什么不能只靠 Math.random 做结果摇色子的视觉表现是“骰子在转”但业务逻辑的核心是“停下来时显示哪个点数”。如果你直接写Math.floor(Math.random() * 6) 1然后立刻把数字更新到页面上用户看到的效果就是数字闪烁一下完全没有“摇”的过程。真正的小程序端摇色子需要的是两套状态动画期间的高频点数切换和动画结束后的最终点数值。这两套状态对应两个不同的变量不能共用一个 data 字段。我在做这类需求时会在data里拆成displayValue和resultValue两个字段。动画期间不停修改displayValue动画结束再把resultValue赋给displayValue。这样做的原因有两个一是避免动画中途用户误触导致最终结果被干扰二是方便后续开发“连续摇 N 次”或“开牌”玩法时结果历史可以被独立记录。另一个值得注意的细节是Math.random的随机性问题。在大部分场景下它够用但如果你在动画帧回调里连续调用多次某些 iOS 版本下会出现短周期重复的序列。常见做法是每次摇之前先用时间戳做一次种子扰动而不是在动画回调里直接依赖Math.random做大量消费。function getRandomPoint() { const seed Date.now() % 1000; const randomVal (Math.random() * 1000 seed) % 6; return Math.floor(randomVal) 1; }代码里有两点值得说明。第一Date.now() % 1000产生一个 0 到 999 的抖动值加到Math.random结果上再取模可以让连续快速点击时的结果分布更均匀。第二返回值用Math.floor取整后加 1得到 1 到 6 的整数这是色子点数的标准映射。如果你希望某些点数出现概率略高可以调整取模的区间权重但在普通场景下没必要。2.2 动画层选型CSS transform 还是 Canvas摇色子的动画层有两种主流做法用 CSS 的transform: rotate做骰子图标旋转或者用 Canvas 渲染点数。CSS 方案的优势在于实现简单、性能开销低30 行左右的 WXSS 就能完成。Canvas 方案的优势在于可以自由绘制任意点数样式、做更细腻的粒子效果但代码量会翻倍。我一般建议 60 分项目用 CSS目的是快速跑通流程想拿这个项目做毕业设计或作品集再上 Canvas。下面是一组可直接运行的关键样式片段.dice { width: 200rpx; height: 200rpx; border-radius: 24rpx; background: #fff; box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12); transition: transform 0.1s linear; } .dice-rolling { animation: diceShake 0.4s infinite; } keyframes diceShake { 0% { transform: rotate(0deg) scale(1); } 25% { transform: rotate(90deg) scale(1.1); } 50% { transform: rotate(180deg) scale(0.95); } 75% { transform: rotate(270deg) scale(1.05); } 100% { transform: rotate(360deg) scale(1); } }这里的transition: transform 0.1s linear是为了让每次旋转的中间帧有平滑过渡而不是瞬移。diceShake动画持续 0.4 秒并无限循环配合 JS 里的animationend事件或setTimeout在 1.2 秒后移除该 class就可以模拟“摇 3 下再停下来”的效果。需要注意小程序的 WXSS 支持 CSS 动画但keyframes中部分属性如filter在 iOS 旧版本上可能无效所以旋转和缩放是最稳妥的组合。2.3 事件循环与 setData别把每帧都发给视图层小程序的双线程模型决定了setData走的是一条「逻辑层到视图层」的异步通道。如果你在动画期间用setInterval每 16 毫秒发一次setData在低端安卓机上会出现视图层卡顿甚至触发 “setData 数据量过大” 的告警。标准做法是控制频率每秒 8 到 10 次是安全区间。我的做法是动画期间只传一个displayValue且数值在 1 到 6 之间循环取随机动画结束只传一次resultValue。这样单次setData的数据量只有几个字节非常适合这个场景。如果你想要更华丽的效果可以额外传一个rollingIndex用来改变骰子背景色但不要传数组或嵌套对象。3. 从零搭建摇色子页面WXML、WXSS 到 JS 的完整实现3.1 页面结构与样式一套可以直接抄的微信小程序布局先建一个标准的微信小程序页面目录包含index.wxml、index.wxss、index.js、index.json四个文件。index.json里设置导航栏标题为“摇色子”并开启下拉刷新虽然本场景用不到但作为项目实例可以演示配置项。{ navigationBarTitleText: 摇色子, enablePullDownRefresh: false, disableScroll: false }下面是 WXML 结构。核心是一个骰子容器、一个结果显示区域和一个按钮。view classpage view classdice-area view classdice {{rolling ? dice-rolling : }} bindtaponRollTap text classdice-value{{displayValue}}/text /view /view view classresult-area text本次结果{{resultValue}}/text text次数{{rollCount}}/text /view button classroll-btn bindtaponRollTap摇一摇/button /view这里把点击事件同时挂在骰子和按钮上是因为用户可能习惯直接点骰子。rolling字段控制动画 class 的添加与移除displayValue在动画期间高频变化resultValue是最终展示结果。注意bindtap不要写成catchtap否则在 button 上会阻止事件冒泡导致部分机型点击反馈异常。WXSS 部分除了上一节的骰子样式还需要给页面一个深色背景让白色骰子更醒目.page { min-height: 100vh; background: linear-gradient(135deg, #1a1a2e, #16213e); display: flex; flex-direction: column; align-items: center; padding-top: 100rpx; } .dice-value { font-size: 80rpx; font-weight: 700; color: #333; line-height: 200rpx; text-align: center; display: block; }深色背景的作用不只是美观它还能减少 OLED 屏幕在白色高亮区域上的拖影让骰子旋转动画看起来更干净。line-height: 200rpx是为了让点数文字在骰子容器内垂直居中这里的 200rpx 与.dice的高度保持一致。如果你修改骰子尺寸记得同步调整这个值。3.2 逻辑层实现滚动数字动画与最终结果分离index.js的核心逻辑分三块初始化数据、开始摇动动画、结束动画并写入结果。下面是完整代码可以直接粘贴到index.js里运行。Page({ data: { displayValue: 1, resultValue: 0, rolling: false, rollCount: 0, timer: null }, onRollTap() { if (this.data.rolling) { return; } const that this; this.setData({ rolling: true, resultValue: 0 }); let frameCount 0; const maxFrames 12; this.data.timer setInterval(() { frameCount; const tempValue Math.floor(Math.random() * 6) 1; that.setData({ displayValue: tempValue }); if (frameCount maxFrames) { clearInterval(that.data.timer); const finalValue getRandomPoint(); that.setData({ displayValue: finalValue, resultValue: finalValue, rolling: false, rollCount: that.data.rollCount 1 }); } }, 100); }, onUnload() { if (this.data.timer) { clearInterval(this.data.timer); } } }); function getRandomPoint() { const seed Date.now() % 1000; const randomVal (Math.random() * 1000 seed) % 6; return Math.floor(randomVal) 1; }代码里有几个关键点需要展开说明。第一if (this.data.rolling) return是一个节流保护防止用户在动画未结束时连点导致多个setInterval同时运行。第二setInterval的间隔是 100 毫秒12 帧刚好 1.2 秒这个时长在用户体验上是“摇 3 下”的体感不会太长也不会太短。第三动画结束后用clearInterval清理定时器并把rolling置为 false以便下次点击可以再次触发。第四onUnload里清理定时器是一个容易被忽视的坑如果用户摇到一半就退出页面定时器会继续运行不仅浪费资源还会在极少数情况下报setData的警告。这里故意没有用wx.vibrateShort或音效原因是先把核心逻辑跑通才去叠加体验层的东西。你可以在frameCount maxFrames的分支里加入震动反馈稍后我会专门讲这一段的选型与参数。3.3 常见报错与调试setData 路径错误、定时器泄漏、真机预览空白写摇色子项目可能遇到的第一类报错是setData的路径错误比如你试图用this.setData({ dice.value: 3 })去更新一个不存在的嵌套字段。在小程序里如果 data 中没有定义dice这个对象这种写法会静默失败页面不报错但数据不更新。解决方法是确保data里提前声明好完整的字段结构。第二类常见问题是真机预览时页面白屏或动画卡顿。排查顺序是先看调试器的 Console 有没有报错再看 Network 面板有没有资源加载失败最后确认是否是因为 WXSS 里使用了真机不支持的属性。比如box-shadow在 Android 部分低端机上渲染正常但在某些 WebView 内核下会闪烁可以临时换成border来验证。第三类问题出在重复点击。如果没有this.data.rolling的判断用户快速点两下会启动两个setInterval动画结束后只有一个定时器被清理另一个继续跑导致骰子停在某个中间帧上。这种 bug 在开发工具里很难复现但真机上非常容易出现。你可以在模拟器里用“开启自动预览”连点按钮来测试如果同时出现两个骰子旋转状态就说明节流失效了。4. 进阶实战wx.vibrateShort 音效、完整源码结构与抓包调试4.1 给摇色子加上触摸反馈震动与声音的最佳参数一个摇色子项目如果没有震动和声音总觉得少了点什么。小程序提供了wx.vibrateShort和wx.createInnerAudioContext两个 API。震动要解决的问题是“反馈即时的轻震”和“结束时较重的震感”之间的区别。官方文档里wx.vibrateShort的type参数在 iOS 上只支持heavy、medium、lightAndroid 部分机型会忽略这个参数所以你不能把震动强度完全交给这个 API。我常用的方案是动画开始时调用一次wx.vibrateShort({ type: light })动画结束时调用一次wx.vibrateShort({ type: medium })。如果你的项目面向低端安卓机可以把结束震动换成wx.vibrateLong它的震动时长约 400 毫秒体感更明显但也会让用户觉得“手机在响”所以看场景取舍。声音部分使用wx.createInnerAudioContext创建音频实例比wx.createAudioContext更灵活。你不需要真的去准备一个色子音频文件直接用系统默认的点击声或从免费音效库下载一个短 wav 即可。关键点在于音频实例是全局单例还是局部创建。摇色子这种触发频率高的场景建议在onLoad里创建一次页面卸载时销毁避免每次点击都新建实例造成内存碎片。4.2 完整项目实例的源码结构从页面到自定义组件的拆分如果你的摇色子项目想继续扩展比如加上“猜大小”玩法或历史记录面板代码就会从单个页面膨胀到多个页面。这时候就该考虑拆组件了。但拆组件不是越早越好一个页面只有 2 个交互点的时候拆组件反而增加通信成本。常见的拆分时机是同一块 UI 在页面中出现 2 次以上或者这块 UI 的逻辑需要被多个页面复用。在摇色子这个例子里骰子视图可以被拆成一个dice-view自定义组件属性接收value和rolling两个字段内部只负责渲染和 CSS 动画对外派发rollend事件。页面只需要监听这个事件来处理结果和累计计数。下面是最小自定义组件的properties定义Component({ properties: { value: { type: Number, value: 1 }, rolling: { type: Boolean, value: false } }, methods: { handleTap() { this.triggerEvent(roll); } } });自定义组件的关键在于把“内部视觉”和“外部业务”解耦。组件自己不生成随机数只负责展示页面负责生成结果并下发。这样将来如果你要做“连续摇 10 次”或“双色子”可以直接复用dice-view只需要在页面里创建两个组件实例并各自绑定不同的数据字段。4.3 抓包调试真机预览与 Charles 配置要点摇色子项目本身没有网络请求但如果你给它加了分享功能或者接入统计 SDK就绕不开网络请求的调试。微信开发者工具里可以直接看到 Network 面板但真机上的网络请求需要抓包工具。Charles 是 Windows 和 macOS 上都用得比较顺手的方案。先用电脑开启 Charles 的 SSL Proxying然后在微信开发者工具里点击“真机调试”手机会生成一个调试二维码。用微信扫码后手机会自动走电脑的代理。关键设置有两处第一Proxy SSL Proxying Settings里必须添加*.servicewechat.com和你的业务域名否则刷出来的全是加密乱码。第二手机和电脑必须在同一局域网内且手机 Wi-Fi 的代理地址指向电脑的局域网 IP。这里特别提醒微信小程序的 request 域名必须在小程序管理后台配置合法域名且必须是 HTTPS。如果你在开发工具里勾选了“不校验合法域名”来绕过这一步真机预览时一定会失败。你可以通过抓包看到request:fail的详细错误信息通常包含域名校验失败或证书错误。把这两个常见原因记牢能省下一个下午的排查时间。4.4 保存附件到本地wx.env.user_data_path 的常规用法如果你的摇色子项目后来加上了“投骰子历史记录”的导出功能就会遇到文件保存的问题。微信小程序的本地文件系统用wx.env.USER_DATA_PATH作为根目录这是一个常量指向用户数据目录不需要你手动创建。写入文件用wx.getFileSystemManager().writeFile但要注意该接口的路径参数必须以wx.env.USER_DATA_PATH开头否则会报权限错误。常见的数据导出格式有两种JSON 和 Excel。小程序原生 API 不支持直接写 xlsx 文件通常的做法是在前端拼 CSV 字符串然后用writeFile写入.csv后缀的文件。CSV 可以用 Excel 直接打开不需要引入任何库。字节大小方面几百条记录完全不用担心性能但如果你要导出几万条就应当考虑分批写入或用云开发云端生成文件再下载。5. 收尾技巧把摇色子项目从“能跑”打磨成“能分享”5.1 分享卡片带参动态标题没过审的原因摇色子项目做出来之后大概率会想转发到群里。微信小程序的onShareAppMessage可以自定义分享标题、图片路径和路径参数。很多新手在这里踩坑他们用path: /pages/index/index?result5想把自己摇出来的点数带给接收方结果发现接收方进入页面后拿不到参数。原因是onShareAppMessage里的path是相对路径接收方打开时会执行对应页面的onLoad参数确实会带过去。但如果接收方的小程序未启动微信会先加载页面再执行onLoad这是正常的问题通常出在开发者工具里“分享到好友”后接收方打开的是旧的的编译版本。解决办法是在分享回调的success里打日志或者用wx.showShareMenu开启带withShareTicket的分享来排查。真机上分享后务必用另一个微信账号测试别用自己的号反复试。另外一个审核相关的坑分享标题不要写成“快来跟我摇色子你也会赢”。涉及“赢”“奖”“红包”这些词在小程序审核时容易触发营销资质审核。标题用中性描述比如“我一口气摇了 6 个 6你要不要试试”被拒的概率会小很多但这属于平台运营规范范畴具体尺度以官方现有审核规则为准。5.2 用 wx.showShareMenu 提升二次曝光率为了让用户愿意主动分享小程序提供wx.showShareMenu可以在页面右上角菜单中显示“转发”按钮。对于摇色子这种轻娱乐项目还可以配合wx.updateShareMenu设置withShareTicket: true这样获取到分享群的唯一标识可以做群排行。但要注意withShareTicket只在分享到微信群时有效分享给个人不会返回 ticket。onLoad() { wx.showShareMenu({ menus: [shareAppMessage, shareTimeline], success() { // 菜单显示成功 } }); }, onShareAppMessage() { return { title: 摇色子看你今天的手气, path: /pages/index/index }; }这段代码里的menus数组可以同时打开“转发给好友”和“分享到朋友圈”两个入口。朋友圈分享是小程序 appid 专属能力个人开发者的账号需要满足一定条件才能开通不是所有小程序都能直接使用。如果你的后台找不到该开关说明当前账号类型不支持不必纠结。5.3 用一个隐藏功能提高留存记录最近 N 次摇色子结果在页面底部加一个卡片区域展示最近 5 次摇到的点数用wx.setStorageSync存到本地。这个功能的代码量不大但会让项目实例显得完整毕业设计答辩时也更有话讲。saveHistory(finalValue) { const history wx.getStorageSync(diceHistory) || []; history.unshift(finalValue); if (history.length 5) { history.pop(); } wx.setStorageSync(diceHistory, history); this.setData({ history: history }); }unshift把最新结果放到数组开头pop删除最旧的记录这样一个简单的循环队列就完成了。存本地而不是存内存是为了用户下次打开小程序还能看到上一次的手气记录。这个小功能在代码评审里是一个很好的“产品思维”体现不是做完一个点击动画就结束了而是把用户的行为沉淀下来。如果你想把历史记录导出为 CSV前一节提到的wx.env.USER_DATA_PATH就是标准的落盘路径二者可以无缝衔接。本文还有配套的精品资源点击获取
返回列表