
简介面向社交聚会场景的微信小程序源码项目以随机数生成算法模拟摇骰子过程适用于小程序初学者、前端开发者以及想为酒局增添趣味的普通用户。压缩包共一百八十个文件体积仅一点六八兆内部按页面与素材划分图片与音频资源丰富同时包含完整的配置、逻辑、结构及样式文件可直接导入微信开发者工具运行预览。目前已有九百二十人学习下载使用。代码中采用随机数函数生成一到六的点数借助数组与循环处理多颗骰子同时完成页面布局、交互反馈和规则设定如大冒险、真心话等玩法能清晰展示小程序从界面到逻辑的完整构建思路还可在此基础上进行二次开发定制专属的饮酒游戏规则。1. 酒局上三个人同时摇三粒骰子随机数不公平时最吃亏酒局上三个人同时摇三粒骰子点数最小的人喝酒这个场景做成微信小程序听起来只是把骰子搬上屏幕但真正动手就发现第一个问题随机数怎么生成才不至于让某些点数频繁出现。“喝酒摇色子小程序.zip”这个源码包正好给了你一套可运行的原生小程序项目内页除了摇骰子还有转盘、弹幕等酒局玩法模块。不想从头设计界面的人可以直接拿它当底座想学微信小程序开发的人则能从 index.js 里看到 Page 生命周期、事件绑定和数据驱动的完整结构。接下来不谈论下载按源码的模块边界拆算法和工程结构把能复用的代码片段抽出来讲清楚。2. 骰子点数的生成算法从 Math.random() 到多骰子模型2.1 微信小程序里如何生成 1~6 的随机数几乎所有编程语言的随机函数都是伪随机微信小程序中的Math.random()会返回一个[0, 1)区间内的浮点数闭区间左侧是 0右侧永远到不了 1。想要得到 1 到 6 之间的整数标准写法是向下取整后加 1function rollSingleDice() { return Math.floor(Math.random() * 6) 1; }这里先乘 6得到[0, 6)的浮点数再用Math.floor()向下取整成 0 到 5 的整数最后 1把区间移动到 1 到 6。很多人会顺手写Math.ceil(Math.random() * 6)这在小概率事件上有隐患如果随机数等于 0结果会变成 0骰子面数就不合法了。另一种Math.round(Math.random() * 6)会让 1 和 6 出现的概率比中间点数低因为四舍五入的边界被切到了两侧。下表把这几种常见写法放在一起对比写法表达式可能结果概率特征向下取整Math.floor(Math.random() * 6) 11~6均匀向上取整Math.ceil(Math.random() * 6)0~6随机数为 0 时出现 0四舍五入Math.round(Math.random() * 6)0~61 和 6 概率偏低丢弃小数parseInt(Math.random() * 6) 11~6均匀但可读性差从表中能看到parseInt虽然结果也是均匀但它在解析负数时行为不同且需要 1才能对齐骰子面数。项目中这种生成语句往往会包一层函数方便后续换成加密随机源。2.2 多骰子同时摇数组、循环与结果累计实际酒局通常是三个骰子一起摇点数还要累加或者比大小。单一变量存不下多个骰子常规做法是用数组保存每一次的结果再通过循环生成function rollMultipleDice(count) { const values []; for (let i 0; i count; i) { values.push(Math.floor(Math.random() * 6) 1); } return values; }count是骰子数量循环里每次生成的数都会被放进数组。用数组的好处是后续渲染可以直接使用wx:for点数求和也可以用reduce一行完成而不是定义dice1、dice2、dice3三个变量。放到小程序 Page 里还要考虑用户手速过快的问题。连续点击“摇骰子”按钮会触发多次生成动画还没结束就开始下一次体验很差。所以要在 data 里加一个状态锁Page({ data: { dices: [1, 1, 1], rolling: false }, roll() { if (this.data.rolling) return; this.setData({ rolling: true }); const dices rollMultipleDice(this.data.dices.length); this.setData({ dices: dices, rolling: false }); } })rolling为true时直接返回避免重复触发。这里有一点容易被忽略setData是异步渲染如果把rolling: false也放到同一次setData里按钮看起来会被立刻解锁实际动画却可能还在播放。更稳妥的写法是在动画完成回调里再解锁后面讲转盘时会再提到。2.3 公平性边界伪随机、种子与真实随机源Math.random()的本质是伪随机数生成器它基于一个内部种子种子确定后序列也就确定了。对喝酒摇骰子这种娱乐场景完全够用。但如果要做积分排行榜、抽奖翻倍就要考虑更严格的随机来源。微信小程序基础库提供了wx.getRandomValues底层走系统级随机源适合对公平性要求更高的场景。在多骰子场景里还有一个概率陷阱多个骰子的点数之和并不是均匀分布。例如三个骰子点数总和为 3 或 18 的概率是1/216而和为 10 或 11 的概率远高于它们。如果规则是“点数总和最小的人喝酒”不要尝试提前把总和均匀化那是反概率的。真正需要均匀化的只是单颗骰子的每一面。用取模运算处理非标准骰子面数也要小心当随机范围不能被面数整除时余数会导致部分点数概率偏高此时应该用拒绝采样也就是生成后如果落在无效范围内就重新生成。3. 源码包拆解从 zip 到可跑起来的微信小程序项目3.1 文件清单图片、脚本和页面角色对齐压缩包解压后能看到一批 jpg 和 js 文件文件名直接暴露了模块边界。下面是一个可执行的对照表具体逻辑还要打开源码确认但从命名能反推它们的分工文件类型推测作用index.jsjs摇骰子主页面逻辑zhuanpan.jsjs转盘转动逻辑danmu.jsjs弹幕文字滚动逻辑eyu.jsjs鳄鱼或额鱼主题玩法逻辑dage.jsjs大哥模式或点数排行逻辑zhijian.jsjs指尖互动或戳戳玩法逻辑fximages.jpg图片骰子点数或界面元素wxchar.jpg图片微信头像或聊天背景zhijian.jpg图片指尖玩法的背景图lunpanbj.jpg图片转盘模块背景图这类命名方式在小程序源码里很常见没有统一的utils.js把所有函数堆在一起而是按页面独立成文件。好处是删改一个玩法不会影响其他页面坏处是公共状态需要自己抽离。导入项目后第一件事不是急着看代码而是先把这些文件归属到对应页面目录里避免在分包时出现循环依赖。3.2 app.json 与页面路由注册原生小程序启动时会先读取根目录下的 app.json其中pages数组的第一个路径就是首页。如果压缩包里的页面没有注册在这个数组里开发者工具会在编译时报错page not found。一个典型的配置如下{ pages: [ pages/index/index, pages/zhuanpan/zhuanpan, pages/danmu/danmu ], window: { navigationBarTitleText: 酒局骰子, navigationBarBackgroundColor: #1a1a2e, navigationBarTextStyle: white } }这段代码里需要注意三点第一pages路径不要写后缀工具会自动补全 wxml、wxss、js 和 json 文件第二navigationBarTitleText是全局默认标题可以在运行时被wx.setNavigationBarTitle覆盖这也是和热搜词“小程序动态设置标题”相关的核心点第三颜色值只能用十六进制不能写 CSS 变量。看到白屏时优先检查 app.json 是否 JSON 格式错误少一个逗号都会导致整个项目编译失败。3.3 WXML 和 WXSS 的界面结构背景图与布局摇骰子页面通常由一组骰子视图和一个触发按钮组成WXML 里通过wx:for遍历dices数组view classdice-container view classdice wx:for{{dices}} wx:keyindex image src/images/fximages.jpg classdice-bg / text classdice-num{{item}}/text /view button bindtaproll摇骰子/button /view这里src使用了绝对路径/images/...如果图片放在页面目录下也可以写相对路径。但真机调试时相对路径容易因为分包加载而失效建议统一放到项目根目录的 images 文件夹。WXSS 里用 rpx 做响应式适配.dice { width: 120rpx; height: 120rpx; position: relative; margin: 20rpx; } .dice-bg { width: 100%; height: 100%; } .dice-num { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 40rpx; color: #fff; }注意数值大小要放在图片之上否则背景图会盖住点数。3.4 微信开发者工具导入与调试从 zip 到可运行状态只需要四步先解压压缩包注意目录不要带中文然后打开微信开发者工具选择“导入项目”接着指向解压后的根目录AppID 可以先使用测试号最后点“编译”。如果工具提示找不到 app.json大概率是把外层文件夹也包进去了检查导入目录是否直接包含 app.json 文件。进入调试界面后Console 面板会输出所有 JS 异常Network 面板能看到图片和接口加载情况。很多从网上下载的源码会因为基础库版本过旧而报写法错误此时在详情面板里把调试基础库切到2.33.0或更高版本即可。这个动作看着简单却能过滤掉一半的“代码运行异常”。4. 酒局玩法的交互逻辑规则设定、积分记录与动画反馈4.1 骰子动画与弹幕效果 danmu.js 的作用danmu.js 管理的是一段段从底部冒出的文字给酒局增加气氛。弹幕的核心是异步追加节点并在动画完成后移除节点否则页面节点数量会无限增长。常见做法是每一条弹幕对象包含唯一 id、文本和垂直位置function pushDanmu(text) { const id dm_ Date.now(); const item { id: id, text: text, top: Math.floor(Math.random() * 50) }; return item; }在页面里拿到这个对象后把它 push 到 data 数组通过wx:for渲染。动画结束后用setTimeout从数组中删除对应 id 项。这里最关键的是Date.now()作为 id如果两条弹幕在同一毫秒生成会出现 key 冲突更稳妥的做法是在后面追加一个自增计数器。4.2 积分和规则如何用全局变量与缓存实现喝酒玩法通常有“输家喝、赢家指定”等规则积分记录是其中一个常见功能。原生小程序没有持久化 store最简单可靠的方式是wx.setStorageSync和wx.getStorageSyncfunction addScore(points) { const old wx.getStorageSync(score) || 0; const total old points; wx.setStorageSync(score, total); return total; }参数points是整局累计的增量可以是负数。读取时用|| 0处理首次启动没缓存的情况避免计算出NaN。多个玩法模块共用一个 key 会互相覆盖建议按玩法加前缀例如zhuanpan_score和dice_score。下表列出推荐使用的存储键存储键保存内容读写时机dice_score摇骰子累计积分每次结算后写入dice_rule自定义规则选项游戏开始前保存last_player上一局输家编号页面卸载时保存如果规则选项是多选值建议用 JSON.stringify 转成字符串后再存读取时再 JSON.parse。这样后续扩展“大冒险”“真心话”等模式时不需要频繁加新键。4.3 转盘模块 zhuanpan.js 的旋转算法转盘是酒局里另一个高频玩法。zhuanpan.js 的核心不是随机数而是角度累加和减速。用setInterval模拟惯性转动每次更新角度并让速度按比例衰减let angle 0; let speed 15; const sectorCount 8; const timer setInterval(() { angle speed; speed * 0.95; if (speed 0.5) { clearInterval(timer); const index Math.floor((angle % 360) / (360 / sectorCount)); console.log(命中区域:, index); } drawWheel(angle); }, 16);speed初始值决定转盘圈数初始越大转得越多0.95是减速系数系数越接近 1停止得越慢。命中区域的判断用取模把角度拉回 0 到 360 度再除以每个扇区的角度。这里不能直接Math.random()生成区域否则动画表现和结果对不上玩家会认为游戏造假。在组件渲染上用 CSStransform: rotate(angle deg)比 canvas 重绘效率更高。每帧只改一个角度值性能压力比canvas.draw小很多。4.4 交互边界快速点击、动画锁和状态恢复第 2.2 节提到用rolling锁防连点这个思路同样适用于转盘和弹幕页面。转盘在转动期间如果不加锁用户连点会叠加多个定时器速度越转越快。除了锁状态还需要在页面进入后台时处理定时器onHide() { if (this.timer) { clearInterval(this.timer); this.timer null; } }onHide在小程序切到后台时触发不清掉定时器会导致动画继续空转消耗 CPU。如果想在每次进入玩法页时更换顶部标题可以在onShow里调用wx.setNavigationBarTitlewx.setNavigationBarTitle({ title: 转盘惩罚 });这是“小程序动态设置标题”的标准用法比在 app.json 里固定标题灵活得多尤其适合多玩法模块共用一个页面的场景。5. 从源码到上线性能优化与真机排错5.1 减少 setData 与图片加载优化小程序每次setData都会把数据从逻辑层传到渲染层频繁调用是卡顿的第一来源。很多人在摇骰子动画里让骰子数字每 50ms 更新一次for (let i 0; i 10; i) { this.setData({ diceValue: rollSingleDice() }); }这段代码会让视图层在几百毫秒内被写入十次数据低端机直接掉帧。正确做法是动画阶段用纯 CSS 或定时器只做视觉变换最终结果只用一次setData写入const finalValue rollSingleDice(); this.setData({ diceValue: finalValue });如果必须有中间过程把多次值合并成一个数组一次性提交。图片方面背景图 lunpanbj.jpg 这类大图建议开启懒加载WXML 的image标签加lazy-load属性同时把图片压缩到 200KB 以内避免冷启动时同时解码多张全屏图。5.2 真机调试中常见的预览白屏问题开发者工具模拟器正常真机却白屏这是微信小程序源码复现中最高频的问题。第一优先级是看 Console 报错但常见原因集中在以下四点现象排查方向解决动作白屏且提示 page not foundapp.json 路由缺失检查 pages 数组路径切后台再回来白屏页面 onShow 异常加 try catch 隔离仅某个页面白屏该页面 json 配置不合法检查组件路径和 usingComponents图片全部不显示资源路径大小写错误统一使用绝对路径并小写文件名在“修改刚进入的加载页面”时也要注意onLoad生命周期里不要做例如读取大图片到 canvas 这类重操作应先渲染骨架等onReady再加载数据。5.3 怎么安全处理 zip 压缩包里的旧版本资源解压后第一件事是校验文件完整性尤其是从网上下载的源码可能存在文件缺失或中间人修改。用命令统计文件数量和校验关键文件unzip drink-dice.zip -d drink-dice find drink-dice -type f | wc -l ls -la drink-dice/pages/index/index.js对比压缩包内文件数和解压后数量如果数量不一致说明解压过程中有失败项。对于多版本目录可以用diff -rq对比两次解压结果而不是直接覆盖避免把旧资源残留进微信开发者工具缓存。6. 一个提高酒局体验的小技巧在骰子停止前增加声效与振动反馈6.1 用 wx.vibrateShort 与 InnerAudioContext 组合摇骰子缺少物理触感玩家的兴奋感会打折。可以在roll方法里同时触发音频和振动让“停止”时刻有明确的物理反馈。核心代码用wx.createInnerAudioContext播放短音效用wx.vibrateShort让手机震动const audioCtx wx.createInnerAudioContext(); audioCtx.src /audio/dice_stop.mp3; audioCtx.play(); setTimeout(() { wx.vibrateShort({ type: heavy, fail: () { wx.vibrateShort({ type: medium }); } }); }, 300);type参数在 iOS 和 Android 上有差异heavy是较重的震动部分 Android 机型不支持时会在 fail 回调里降级成medium。300ms的延迟是为了等骰子动画走到人眼感受到的“停止”瞬间太早震效果像提示音太晚又和视觉脱节。6.2 验证效果与真机注意事项使用开发者工具模拟器无法测试振动必须在真机调试里验证。打开手机设置中的系统触感反馈否则wx.vibrateShort会静默失败。音频要确认文件格式和大小推荐 m4a 或 mp3时长控制在 0.5 秒以内。页面卸载时记得销毁音频上下文onUnload() { if (audioCtx) { audioCtx.destroy(); } }InnerAudioContext不销毁会持续占用系统音频资源多次进入页面后累计多个实例最终导致声音通道被占满。调用wx.vibrateShort前也建议用wx.getSystemInfoSync().platform判断运行平台iOS 对heavy的响应比 Android 稳定Android 上优先使用medium减少电机发热。本文还有配套的精品资源点击获取