uni-app offCanplay无法取消监听

uni-app offCanplay无法取消监听

信息类别 详情
产品分类 uniapp/App
PC开发环境 Windows
PC操作系统版本 win 10 2004
HBuilderX类型 正式
HBuilderX版本 3.2.12
手机系统 全部
手机厂商 苹果
页面类型 vue
vue版本 vue2
打包方式 云端
项目创建方式 HBuilderX

测试过的手机

  • iphonese2

示例代码

<template>  
    <view class="voiceIntroduction_box">  
        <view class="voice_icon">  
            <view :class="{'cmd_progress':true,'progress_active':process}">  
                <u-circle-progress :percent="process" :inactive-color="originalColor" :active-color="processColor"  
                    width="452" border-width="10" :duration="duration"></u-circle-progress>  
            </view>  
            <view class="voice_icon_inside">  
                <view class="voice_mic">  
                    <image src="../static/image/mic.png" mode=""></image>  
                </view>  
                <view class="voice_time">  
                    00:{{recorded.time}}  
                </view>  
            </view>  

        </view>  
        <view class="voice_btn">  
            <view v-show="recorded.show" @click.stop="handleRecord()" class="voice_again btn">  
                <image src="../static/image/again.png" mode="">  
                    <text>重新录制</text>  
                </image>  
            </view>  
            <view @click.stop="handleClick()" class="voice_play btn">  
                <image :src="recorded.src" mode="">  
                    <text>{{recorded.text}}</text>  
                </image>  
            </view>  
            <view v-show="recorded.show" @click.stop="uploadMp3Action()" class="voice_detertime btn">  
                <image src='../static/image/determine.png' mode="">  
                    <text>确定</text>  
                </image>  
            </view>  
        </view>  
        {{process}}  
        <view class="person">  
            <textarea v-model="personal" placeholder="个人简介(自动保存)" placeholder-style="color:#999999" />  
            <image src="../../static/image/edit2.png" mode=""></image>  
        </view>  

    </view>  
</template>  

<script>  
import permision from '@/js_sdk/wa-permission/permission.js'  
const recorderManager = uni.getRecorderManager()  
const innerAudioContext = uni.createInnerAudioContext()  
innerAudioContext.autoplay = true  

export default {  

    data() {  
        return {  
            recorded: {  
                show: false,  
                text: '点击录音',  
                time: '00',  
                src: '../static/image/play(1).png',  
            },  
            timer1: null,  
            timer2: null,  
            timer3: null,  
            timer4: null,  
            process: 0, //录制进度  
            processColor: '#6FECE4', //进度条颜色  
            originalColor: '#ffffff', //进度条底色  
            // innerbgColor: '#111',  
            time: null,  
            maxTime: 10000, //最大时间  
            duration: 10000, //进度条走一周需要的时间  
            voicePath: '', //录音文件路径  
            voiceStatus: 0, //录音状态  
        }  
    },  
    onLoad() {  
        if (this.$store.getters.getData.voice) {  
            this.voicePath = this.$store.getters.getData.voice  
            innerAudioContext.src = this.voicePath  
            innerAudioContext.onCanplay((res) => {  
                this.recorded.time = '0' + Math.floor(innerAudioContext.duration)  
                this.time = this.recorded.time  
                console.log(innerAudioContext);  
                console.log(2);  
                innerAudioContext.offCanplay(rej => {  
                    console.log(1111);  
                })  
            })  
            this.finishRecord()  
        }  

        // console.log(this.time);  
        recorderManager.onStop((res) => {  
            this.voicePath = res.tempFilePath  
        })  
    },  
    computed: {  
        personal: {  
            get: function() {  
                return this.$store.getters.getData.personal_profile  
            },  
            set: function(data) {  
                this.$HTTP('/api/technician_index/edit_user_info', {  
                    type: 8,  
                    personal_profile: data  
                }, 'POST', true).then(res => {  
                    console.log(res);  
                })  
            }  
        }  
    },  
    methods: {  
        handleClick() {  
            function judgeIosPermissionRecord() {  
                var result = false;  
                var avaudiosession = plus.ios.import("AVAudioSession");  
                var avaudio = avaudiosession.sharedInstance();  
                var permissionStatus = avaudio.recordPermission();  
                if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {  
                    uni.showModal({  
                        title: '提示',  
                        content: '录音权限已被禁用,是否前往开启?',  
                        success: function(res) {  
                            if (res.confirm) {  
                                permision.gotoAppPermissionSetting()  
                            } else if (res.cancel) {  
                                console.log('用户点击取消')  
                            }  
                        }  
                    })  
                } else {  
                    result = true;  
                    voFun()  
                    console.log("麦克风权限已经开启");  
                }  
                plus.ios.deleteObject(avaudiosession);  
                return result;  
            }  
            permision.requestAndroidPermission('android.permission.RECORD_AUDIO').then(resolve => {  
                // console.log(JSON.stringify(resolve))  
                if (resolve == -1) {  
                    uni.showModal({  
                        title: '提示',  
                        content: '录音权限已被禁用,是否前往开启?',  
                        success: function(res) {  
                            if (res.confirm) {  
                                permision.gotoAppPermissionSetting()  
                            } else if (res.cancel) {  
                                console.log('用户点击取消')  
                            }  
                        }  
                    })  
                } else if (resolve == 1) {  
                    voFun()  
                }  
            }).catch(reject => {  
                console.log(JSON.stringify(reject))  
            })  

            let voFun = () => {  
                // console.log(this.voiceStatus);  
                // 开始录音  
                if (this.voiceStatus == 0) {  
                    this.startRecord()  
                    // 结束录音  
                } else if (this.voiceStatus == 1) {  
                    this.finishRecord()  
                    console.log(1);  
                    // 试听录音  
                } else if (this.voiceStatus == 2) { //点击了试听录音  
                    this.audition()  
                    // 暂停/结束 试听  
                } else if (this.voiceStatus == 3) {  
                    this.pauseAudition()  
                }  
            }  
            if (uni.getSystemInfoSync().brand == 'Apple') {  
                judgeIosPermissionRecord()  
            }  
        },  

        // 未录制状态  
        unRecord() {  
            this.voiceStatus = 0  
            innerAudioContext.stop()  
            this.voicePath = ''  
            innerAudioContext.src = this.voicePath  
            this.recorded.show = false  
            this.recorded.text = '点击录音'  
            this.recorded.time = '00'  
            this.time = null  
            this.recorded.src = '../static/image/play(1).png'  
            this.process = 0  

        },  
        // 开始录制状态  
        startRecord() {  
            this.voiceStatus = 1 //正在录制  
            this.recorded.src = '../static/image/finish.png'  
            console.log(1);  
            recorderManager.start(this.maxTime)  
            let v_time = 0  
            this.timer1 = setInterval(() => {  
                v_time++  
                if (v_time <= 9) {  
                    this.recorded.time = '0' + v_time  
                } else {  
                    this.recorded.time = v_time  
                }  
                this.time = v_time  
            }, 1000)  
            this.timer2 = setInterval(() => {  
                this.process += 1000 / this.maxTime  
                // console.log(this.process);  
                // 最大值为maxTime  
                if (this.process >= 100) {  
                    this.process = 100  
                    recorderManager.stop()  
                    this.recorded.src = '../static/image/play.png'  
                    this.recorded.show = true  
                    clearInterval(this.timer2)  
                    clearInterval(this.timer1)  
                    this.recorded.text = '点击试听'  
                }  
            }, 10)  
            this.recorded.show = false  
            this.recorded.text = '点击结束录音'  
        },  
        // 结束录制状态  
        finishRecord() {  

            recorderManager.stop()  
            this.voiceStatus = 2 //点击了结束录音,此时可以试听  
            this.recorded.src = '../static/image/play.png'  
            this.recorded.show = true  
            this.time = this.recorded.time  
            clearInterval(this.timer2)  
            clearInterval(this.timer1)  
            this.recorded.text = '点击试听'  
        },  
        // 试听状态  
        audition() {  
            this.voiceStatus = 3  
            this.duration = this.time  
            this.process = 0  

            if (this.voicePath) {  
                innerAudioContext.src = this.voicePath  
                innerAudioContext.play()  
            }  
            this.recorded.src = '../static/image/pause.png'  
            // time?time:time= this.recorded.time;  
            this.recorded.time = '00'  
            this.recorded.text = '播放中'  
            let v_time = 0  

            let fn = () => {  
                // console.log(this.timer3);  
                if (v_time <= 9) {  
                    this.recorded.time = '0' + v_time  
                } else {  
                    this.recorded.time = v_time  
                }  
                if (v_time >= this.time) {  
                    this.process = 100  
                    this.recorded.src = '../static/image/play.png'  
                    this.voiceStatus = 2  
                    clearInterval(this.timer3)  
                    this.recorded.text = '点击试听'  
                }  
                // console.log(this.process);  
                v_time++  
                return fn  
            }  
            this.timer3 = setInterval(fn(), 1000)  
            this.$nextTick(() => {  
                this.timer4 = setInterval(() => {  
                    this.process += 1 / this.time  
                    if (this.process >= 100) {  
                        this.process = 100  
                        clearInterval(this.timer4)  
                    }  
                }, 10)  
            })  
        },  
        //暂停试听状态  
        pauseAudition() {  
            this.voiceStatus = 2 //暂停/结束 试听  
            innerAudioContext.pause()  
            if (this.voicePath) {  
                innerAudioContext.src = ''  
                innerAudioContext.src = this.voicePath  
                innerAudioContext.pause()  
            }  
            this.recorded.src = '../static/image/play.png'  
            this.recorded.text = '点击试听'  
            this.recorded.time = this.time  
            // console.log(this.recorded.time);  
            clearInterval(this.timer1)  
            clearInterval(this.timer2)  
            clearInterval(this.timer3)  
            this.process = 100  
        },  
        // 重新录制  
        handleRecord() {  
            this.unRecord()  
            clearInterval(this.timer1)  
            clearInterval(this.timer2)  
        },  
        uploadMp3Action() {  
            const tempFilePaths = this.voicePath;  
            uni.uploadFile({  
                url: this.$baseUrl + '/api/common/voice_upload', //仅为示例,非真实的接口地址  
                filePath: tempFilePaths,  
                name: 'voiceurl',  
                header: {  
                    'Api-Token': uni.getStorageSync('user_token')  
                },  
                success: (res) => {  
                    uni.showLoading({  
                        title: '上传中'  
                    })  
                    console.log(JSON.parse(res.data).data.voice_url);  
                    this.$HTTP('/api/technician_index/edit_user_info', {  
                        type: 3,  
                        voice: JSON.parse(res.data).data.voice_url  
                    }, "POST", true).then(res => {  
                        uni.hideLoading()  
                        if (res.data.code === 1000) {  
                            uni.showToast({  
                                title: '上传成功',  
                                icon: 'success',  
                                duration: 1000  
                            })  
                            setTimeout(() => {  
                                uni.navigateBack({  
                                    delta: 1  
                                })  
                            }, 1000)  
                        } else {  
                            uni.showToast({  
                                title: '上传失败',  
                                icon: 'error',  
                                duration: 1000  
                            })  
                        }  

                    })  
                }  
            });  
        },  
    },  

}  
</script>  

<style lang="less" scoped>  
    .voiceIntroduction_box {  
        text-align: center;  
        padding-top: 90rpx;  

        .voice_icon {  
            position: relative;  
            margin: auto;  
            width: 440rpx;  
            height: 440rpx;  
            background: #ffffff;  
            border-radius: 50%;  
            border: 2rpx solid rgba(111, 236, 228, 0.31);  
            margin-bottom: 145rpx;  

            .cmd_progress {  
                display: flex;  
                justify-content: center;  
                position: relative;  
                top: -6rpx;  
                left: 0rpx;  
                opacity: 0;  
            }  

            .progress_active {  
                opacity: 1;  
                transition: 10ms;  
            }  

            .voice_icon_inside {  
                position: absolute;  
                top: 30rpx;  
                left: 30rpx;  
                width: 380rpx;  
                height: 380rpx;  
                background: #FFFFFF;  
                box-shadow: 0px 0px 20px rgba(111, 236, 228, 0.31);  
                border-radius: 50%;  
                opacity: 1;  

                .voice_mic {  
                    margin-top: 115rpx;  

                    image {  
                        width: 100rpx;  
                        height: 120rpx;  
                        opacity: 1;  
                    }  

                }  

                .voice_time {  
                    margin: 30rpx;  
                    font-size: 36rpx;  
                    font-weight: 400;  
                    line-height: 50rpx;  
                    color: #666666;  
                    opacity: 1;  
                }  
            }  
        }  

        .voice_btn {  
            display: flex;  
            justify-content: center;  

            .btn {  
                // margin-right: 85rpx;  
                margin-top: 10rpx;  

                image {  
                    width: 140rpx;  
                    height: 140rpx;  
                    display: inline-block;  

                }  

                text {  
                    display: block;  
                    margin-top: 30rpx;  
                    font-size: 26rpx;  
                    font-weight: 400;  
                    line-height: 37rpx;  
                    color: #CECECE;  
                    opacity: 1;  
                }  

            }  

            .voice_detertime {  
                margin-right: 0;  
            }  

            .voice_play {  
                margin-top: 0;  
                margin-left: 85rpx;  
                margin-right: 85rpx;  

                image {  
                    width: 160rpx;  
                    height: 160rpx;  
                }  

            }  
        }  

        .person {  
            margin-top: 159rpx;  
            margin-left: 30rpx;  
            padding: 33rpx;  
            box-sizing: border-box;  
            text-align: left;  
            width: 690rpx;  
            height: 300rpx;  
            background: rgba(111, 236, 228, 0.12);  
            border-radius: 14rpx;  
            position: relative;  

            textarea {  
                width: 100%;  
                height: 100%;  
                font-size: 28rpx;  
                font-family: PingFang SC;  
                font-weight: 400;  
                color: #000000;  
                opacity: 1;  
            }  

            image {  
                position: absolute;  
                width: 37.5rpx;  
                height: 37.5rpx;  
                right: 39rpx;  
                bottom: 30rpx;  
            }  
        }  
    }  
</style>

更多关于uni-app offCanplay无法取消监听的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

app平台目前暂不支持 off 相关 API

更多关于uni-app offCanplay无法取消监听的实战教程也可以访问 https://www.itying.com/category-93-b0.html


文档也没注明,害得我调试很久

回复 陈大雷Q: 已更新文档

根据你的代码分析,offCanplay无法取消监听的问题主要出现在以下位置:

innerAudioContext.onCanplay((res) => {
    this.recorded.time = '0' + Math.floor(innerAudioContext.duration)
    this.time = this.recorded.time
    console.log(innerAudioContext);
    console.log(2);
    innerAudioContext.offCanplay(rej => {
        console.log(1111);
    })
})

问题分析:

  1. offCanplay调用时机错误:你在onCanplay回调内部调用offCanplay,这会导致每次触发canplay事件时都会尝试取消监听,但实际并未真正移除监听器。

  2. offCanplay参数错误offCanplay需要传入之前onCanplay绑定的回调函数引用,而不是一个新的回调函数。

正确做法:

// 在data或组件实例上保存回调引用
data() {
    return {
        canplayCallback: null,
        // ...其他数据
    }
},

onLoad() {
    if (this.$store.getters.getData.voice) {
        this.voicePath = this.$store.getters.getData.voice
        innerAudioContext.src = this.voicePath
        
        // 保存回调函数引用
        this.canplayCallback = (res) => {
            this.recorded.time = '0' + Math.floor(innerAudioContext.duration)
            this.time = this.recorded.time
            console.log(innerAudioContext);
            console.log(2);
            
            // 在需要的时候取消监听
            // innerAudioContext.offCanplay(this.canplayCallback)
        }
        
        // 绑定监听
        innerAudioContext.onCanplay(this.canplayCallback)
        this.finishRecord()
    }
    
    // ...其他代码
},

// 在合适的时机取消监听,比如组件销毁时
onUnload() {
    if (this.canplayCallback) {
        innerAudioContext.offCanplay(this.canplayCallback)
    }
    // 销毁音频实例
    innerAudioContext.destroy()
}

补充建议:

  1. 音频实例管理:你的innerAudioContext是在模块级别创建的,这会导致多个组件实例共享同一个音频实例。建议在组件内部创建:
export default {
    data() {
        return {
            innerAudioContext: null
        }
    },
    created() {
        this.innerAudioContext = uni.createInnerAudioContext()
        this.innerAudioContext.autoplay = true
    },
    onUnload() {
        if (this.innerAudioContext) {
            this.innerAudioContext.destroy()
        }
    }
}
回到顶部