一、案例背景与目标
在微信小程序开发中,设备能力是小程序区别于普通网页的重要特性之一。加速计(Accelerometer)就是其中非常典型的一个:它可以让小程序感知手机的晃动、倾斜等动作,从而实现摇一摇、计步、体感游戏等交互效果。
本文以「变脸游戏」为例,完整演示wx.onAccelerometerChange(function listener)这个 API 的功能与使用方法。案例的最终效果是:
- 小程序运行后,页面上显示一张脸谱图片;
- 点击脸谱时,画面随机切换成另一张脸谱;
- 摇晃手机时,弹出一个消息框提示,同时画面也随机切换成另一张脸谱。
通过这个案例,可以同时掌握图片随机切换、点击事件绑定和加速计监听三个知识点,非常适合作为小程序设备 API 的入门练习。
二、效果拆解
在动手写代码之前,先把交互拆成三个状态,思路会更清晰:
| 触发方式 | 页面表现 | 涉及技术点 |
|---|---|---|
| 初始进入 | 显示默认脸谱 | 数据绑定{{faces[index]}} |
| 点击脸谱 | 随机切换为另一张脸谱 | bind:tap事件 + 随机数 |
| 摇晃手机 | 弹出消息框并随机切换脸谱 | wx.onAccelerometerChange+wx.showToast |
可以看到,点击和摇晃两种触发方式最终都落到同一个动作上:随机换一张脸谱。因此可以把「换脸」逻辑抽成一个独立方法,供两个事件复用,避免代码重复。
三、新建项目与准备图片
3.1 新建小程序项目
打开微信开发者工具,新建一个项目,注意选择:
- 不使用云服务;
- 不使用模板(选择「不使用模板」或空白模板)。
这样生成的项目结构最干净,便于我们逐文件修改。
3.2 准备图片文件
在小程序根目录下新建一个文件夹images,把案例中用到的脸谱图片全部放进去。建议图片命名规范、统一尺寸,例如:
├── app.js ├── app.json ├── app.wxss ├── images/ │ ├── face1.png │ ├── face2.png │ ├── face3.png │ └── face4.png ├── pages/ │ └── index/ │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss └── project.config.json图片数量可以根据素材自行决定,本案例以 4 张为例。图片路径在代码中统一写成/images/face1.png这种以根目录开头的绝对路径,避免相对路径带来的问题。
四、修改 app.json
app.json是小程序的全局配置文件,主要负责页面路径注册和窗口样式。本案例只需要保证pages/index/index被注册为首页即可:
{ "pages": [ "pages/index/index" ], "window": { "navigationBarTitleText": "变脸游戏", "navigationBarBackgroundColor": "#ffffff", "navigationBarTextStyle": "black" }, "style": "v2", "sitemapLocation": "sitemap.json" }其中navigationBarTitleText用于设置顶部导航栏标题,改成「变脸游戏」更贴合案例主题。
五、编写 index.wxml
页面的核心是一个image组件,它负责呈现脸谱。其src属性通过{{faces[index]}}绑定到图片数组,bind:tap绑定点击脸谱的事件函数:
<view class="container"> <image class="face" src="{{faces[index]}}" mode="aspectFit" bind:tap="changeFace" /> <view class="tip">点击脸谱或摇一摇手机试试</view> </view>这里有两个关键点:
{{faces[index]}}:faces是图片路径数组,index是当前显示的下标,二者都来自data,修改index即可驱动视图更新;bind:tap="changeFace":把点击事件交给changeFace方法处理,方法名要与index.js中保持一致。
六、编写 index.wxss 与 app.wxss
index.wxss负责页面级样式,让脸谱居中显示:
.container { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; background-color: #f7f7f7; } .face { width: 480rpx; height: 480rpx; border-radius: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12); } .tip { margin-top: 40rpx; font-size: 28rpx; color: #888888; }app.wxss是全局样式,可以在这里统一页面基础样式,例如:
page { background-color: #f7f7f7; font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif; }使用rpx作为单位可以让脸谱在不同机型上保持相对一致的视觉比例。
七、编写 index.js:核心逻辑
逻辑层需要完成三件事:初始化数据、实现换脸方法、注册加速计监听。
Page({ data: { faces: [ '/images/face1.png', '/images/face2.png', '/images/face3.png', '/images/face4.png' ], index: 0 }, onLoad() { this.initAccelerometer(); }, // 随机切换到另一张脸谱 changeFace() { const total = this.data.faces.length; let next = this.data.index; // 保证切换后的脸谱与当前不同 while (next === this.data.index) { next = Math.floor(Math.random() * total); } this.setData({ index: next }); }, // 注册加速计监听 initAccelerometer() { wx.onAccelerometerChange((res) => { const { x, y, z } = res; // 计算合加速度,超过阈值判定为摇晃 const shake = Math.abs(x) + Math.abs(y) + Math.abs(z); if (shake > 2.5) { wx.showToast({ title: '检测到摇晃,变脸!', icon: 'none', duration: 1000 }); this.changeFace(); } }); }, onUnload() { wx.offAccelerometerChange(); } });几个细节值得说明:
- 避免重复切换:
while (next === this.data.index)保证随机结果与当前下标不同,否则用户可能点了却没变化,体验很差。 - 摇晃判定:
wx.onAccelerometerChange回调返回x、y、z三个方向的加速度值,单位是 g。静止时合加速度约为 1,摇晃时会明显增大,因此用Math.abs(x) + Math.abs(y) + Math.abs(z)与阈值比较即可。 - 及时注销监听:在
onUnload中调用wx.offAccelerometerChange(),避免页面销毁后监听仍然存在,造成内存泄漏或重复触发。
八、wx.onAccelerometerChange 详解
该 API 属于「设备 / 加速计」分类,用于监听加速度数据变化。基本用法如下:
wx.onAccelerometerChange(function listener(res) { console.log(res.x, res.y, res.z); });回调参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
| x | number | X 轴加速度,单位 g |
| y | number | Y 轴加速度,单位 g |
| z | number | Z 轴加速度,单位 g |
使用时有几点注意事项:
- 监听频率默认较高,回调会被频繁触发,因此不要在回调里做重计算或频繁 setData,建议加阈值判断或节流;
- 页面卸载或不再需要时应调用
wx.offAccelerometerChange注销; - 在开发者工具中模拟摇晃效果有限,建议使用真机调试验证。
九、功能扩展:动态显示累计变脸次数
案例作业要求在基本功能基础上扩展:动态显示累计变脸次数。实现思路很简单,在data中增加一个count字段,每次换脸时自增即可。
Page({ data: { faces: [ '/images/face1.png', '/images/face2.png', '/images/face3.png', '/images/face4.png' ], index: 0, count: 0 }, changeFace() { const total = this.data.faces.length; let next = this.data.index; while (next === this.data.index) { next = Math.floor(Math.random() * total); } this.setData({ index: next, count: this.data.count + 1 }); } });同时在index.wxml中增加一行展示:
<view class="tip">已变脸 {{count}} 次</view>由于点击和摇晃都复用了changeFace,计数逻辑只需写一处,两种触发方式都会自动累加,这也是前面抽取公共方法带来的好处。
十、常见问题与调试建议
- 图片不显示:检查
images文件夹是否位于小程序根目录,路径是否以/开头,文件名大小写是否一致。 - 摇晃没反应:开发者工具的加速计模拟能力有限,请使用真机预览;同时确认阈值是否设置过高。
- 频繁弹框:摇晃时回调触发非常密集,建议增加节流控制,例如记录上次触发时间,间隔 1 秒内不重复响应。
- 页面返回后仍触发:确认已在
onUnload中注销监听。
节流的一种简单写法:
initAccelerometer() { let lastTime = 0; wx.onAccelerometerChange((res) => { const now = Date.now(); if (now - lastTime < 1000) return; const shake = Math.abs(res.x) + Math.abs(res.y) + Math.abs(res.z); if (shake > 2.5) { lastTime = now; wx.showToast({ title: '检测到摇晃,变脸!', icon: 'none' }); this.changeFace(); } }); }十一、总结
本文通过「变脸游戏」这一小案例,串联了小程序开发中的几个基础能力:用image组件与数据绑定渲染图片,用bind:tap处理点击事件,用wx.onAccelerometerChange监听设备加速度实现摇一摇交互,并通过抽取公共方法让点击与摇晃复用同一套换脸逻辑。
在此基础上扩展累计变脸次数,只需增加一个计数字段,体现了「逻辑复用 + 数据驱动视图」的小程序开发思路。掌握这个案例后,可以进一步尝试加入音效、动画过渡,或把加速计用于更复杂的体感交互场景。