小程序仿微信聊天按住说话功能

6,284 阅读3分钟

小程序仿微信聊天按住说话功能

实现:按住说话有录音动画,上滑可取消发送,松开发送录音

录音授权判断

# .wxml
<view class="btn {{touchBtn?'hoverBtn':''}}" catch:touchend="onTouchEnd" bind:touchstart="record"
bind:longpress="onLongpress" catch:touchmove="onTouchMove">{{touchBtn?'松开 结束':'按住说话'}}</view>
// # ontouchstart
record() {
    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直到录音停止,而且更换图片的顺序不能是固定的,这样看起来很假,必须随机更换图片
  • 注意:touchendtouchmove要清空定时器,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);
},
// touchstart
record() {
    // # 开始录音...
    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;
    }
},

以下是完整代码

// # .js
import 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})
    },
})