小程序仿微信聊天按住说话功能
实现:按住说话有录音动画,上滑可取消发送,松开发送录音
录音授权判断
# .wxmlview class="btn {{touchBtn?'hoverBtn':''}}" catch:touchend="onTouchEnd" bind:touchstart="record"bind:longpress="onLongpress" catch:touchmove="onTouchMove"{{touchBtn?'松开 结束':'按住说话'}}/view复制代码// # ontouchstartrecord() { const scopeRecord = app.globalData.scopeRecord; // 无授权时获取授权 if (!scopeRecord) { getScopeRecord(); return; } this.data.touched = true; // 触发父组件录音事件,由父组件调用录音API this.triggerEvent('startRecord')},复制代码上滑取消发送,利用滑动距离判断是否取消,
onTouchMove(e) { if (e.touches[0].clientY 520) { // # 取消发送 this.data.cancelRecord = true; wx.showToast({ title: '松开,取消发送', duration: 10000, image: '/page/common/resource/image/cancel.png' }) } else { // # 不取消 this.showMicAni(1); this.data.cancelRecord = false; }},复制代码松开发送 - 快速点击bug
bug:快速点击按钮,一直显示录音中原因:调起录音API需要一定时间,会出现touchstart-touchend-调起录音API,touchend事件在录音前触发了,无法再调起停止录音API
思路:可以在调用录音后用个变量a标识,在!a时执行touchend后则用变量b标识,在调用录音后若有b则再立即停止录音。但该方法涉及太多变量传递(父传子,子传父),就偷了下懒用了个400ms的延时
onTouchEnd(e) { if (!this.data.touched) return; this.data.touched = false; wx.hideToast(); // 延时处理 防止录音未调起就触发停止 setTimeout(() = { this.triggerEvent('stopRecord', { // # 判断是否取消发送 send: !this.data.cancelRecord }); }, 400); }},复制代码- 除了说话中,上滑取消外,还要加多个说话时间太短的提示
- 方案一:我用了小程序的
longpress事件做判断,未触发该事件则属于说话太短;同时,如果在没在触发Longpress前触发了上滑也默认开启录音 - 方案二:touchstart时开始计时,touchend结束计时,用时间来判断
// # 利用长按判断录音是否太短onLongpress() { this.data.recording = true;},// # 上滑时默认录音中onTouchMove(e) { this.data.recording = true; if (e.touches[0].clientY 520) { ... } else { ... }},onTouchEnd(e) { if (this.data.recording) { // # 触发停止... } else { wx.showToast({ title: '说话时间太短', duration: 500, image: '/page/common/resource/image/warn-b.png' }) // # 触发停止... }},复制代码录音动画 — 实现微信聊天录音中可根据音量大小改麦克风音量提示的动画的toast。
- 问题:官方API中录音回调没提供音量数据,无法根据音量来做动画。而且我们使用了
wx.showToast,image无法使用动图,也无法自定义动画 - 思路:既然无法获取音量数据,又需要用户感知有在录音中,还无法做动画。我想了个方法,能不能每隔一段时间换一张image直到录音停止,而且更换图片的顺序不能是固定的,这样看起来很假,必须随机更换图片
- 注意:
touchend和touchmove要清空定时器,touchmove手指回到按钮则要再次调用showMicAni
showMicAni(i) { // # 初始化间隔时间 let delay = 500; let that = this; wx.showToast({ title: '上滑,取消发送', duration: 60000, // # 准备5张麦克风不同状态的png image: '/page/common/resource/image/mic0' + i + '.png' }) that.data.time1 = setTimeout(() = { // util.getRandomInt公共方法,随机生成【1,5】中的整数 i = util.getRandomInt(1, 5) that.showMicAni(i) }, delay);},// touchstartrecord() { // # 开始录音... this.showMicAni(1);},onTouchEnd() { clearTimeout(this.data.time1)},onTouchMove(e) { clearTimeout(this.data.time1) if (e.touches[0].clientY 520) { // # 取消发送... } else { this.showMicAni(1); this.data.cancelRecord = false; }},复制代码以下是完整代码
// # .jsimport wlogin from '../../../common/resource/js/login.js';import util from '../../../../utils/util'let app = getApp();const getScopeRecord = wlogin.recordInpower();Component({ properties: {}, data: { touchBtn: false, }, ready() {}, methods: { // 利用长按判断录音是否太短 onLongpress() { this.data.recording = true; }, // touchstart record() { const scopeRecord = app.globalData.scopeRecord; if (!scopeRecord) { getScopeRecord(); return; } this.data.recording = false; this.data.touched = true; this.data.cancelRecord = false; this.setData({ touchBtn: true }) this.triggerEvent('startRecord') this.showMicAni(1); }, showMicAni(i) { let delay = 500; let that = this; wx.showToast({ title: '上滑,取消发送', duration: 60000, image: '/page/common/resource/image/mic0' + i + '.png' }) that.data.time1 = setTimeout(() = { // 随机生成 i = util.getRandomInt(1, 5) that.showMicAni(i) }, delay); }, onTouchMove(e) { this.data.recording = true; clearTimeout(this.data.time1) if (e.touches[0].clientY 520) { this.data.cancelRecord = true; wx.showToast({ title: '松开,取消发送', duration: 10000, image: '/page/common/resource/image/cancel.png' }) } else { this.showMicAni(1); this.data.cancelRecord = false; } }, onTouchEnd(e) { clearTimeout(this.data.time1) if (this.data.recording) { if (!this.data.touched) return; this.data.touched = false; wx.hideToast(); setTimeout(() = { this.triggerEvent('stopRecord', { send: !this.data.cancelRecord }); }, 400); } else { wx.showToast({ title: '说话时间太短', duration: 500, image: '/page/common/resource/image/warn-b.png' }) // 延时处理 防止录音未调起就触发停止 setTimeout(() = { this.triggerEvent('stopRecord', {send: false}); }, 400); } this.setData({touchBtn: false}) },})复制代码













