vue3+element plus音频播放组件

页面效果如图

组件代码html

<template>
    <!--音频播放组件 -->
    <div class="my-video-group" :style="{width:videoWidth + 'px'}">
        <div class="box u-flex-between">
            <!-- 音频暂停/播放 -->
            <div class="isPlay">
                <el-button type="info" text size="small" @click="togglePlay">
                    <el-icon size="20">
                        <VideoPlay v-if="!isPlaying" />
                        <VideoPause v-else />
                    </el-icon>
                </el-button>
            </div>
            <!-- 音频时长 -->
            <div class="isPlay-time">
                <el-text>{{ formatTime(currentTime) }}</el-text>
                <el-text class="pr5 pl5">/</el-text>
                <el-text>{{ formatTime(duration) }}</el-text>
            </div>
            <!-- 音频进度条 -->
            <div class="isPlay-progess">
                <el-slider
                    v-model="progress"
                    :min="0"
                    :max="100"
                    :step="0.1"
                    :show-tooltip="false"
                    @change="handleProgressChange"
                    style="width: 420px;"
                ></el-slider>
            </div>
            <!-- 音频音量 -->
            <div class="isPlay-voice u-flex">
                <el-dropdown placement="bottom" max-height="220px">
                    <el-button type='info' text='' size="small">
                       <el-icon size="20"><BellFilled /></el-icon>
                    </el-button>
                    <template #dropdown>
                        <el-slider
                            class="mt20 mb20"
                            v-model="volume"
                            size="small"
                            :min="0"
                            :max="100"
                            :step="1"
                            @change="handleVolumeChange"
                            height="160px"
                            vertical 
                        ></el-slider>
                    </template>
                </el-dropdown>
            </div>
        </div>
        <!-- 音频播放器 -->
        <audio ref="audioRef" :src="props.audioSrc" preload="metadata" @canplay="onCanplay"></audio>
    </div>
</template>

js

<script setup>
    import { ref, onMounted, onBeforeUnmount } from 'vue'
    const audioRef = ref(null)
    const props = defineProps({
        videoWidth: {
            type: Number,
            default: 740
        },
        audioSrc: {
            type: String,
            default: 'https://web-ext-storage.dcloud.net.cn/uni-app/ForElise.mp3'
        }
    })

    const isPlaying = ref(false)
    const currentTime = ref(0)
    const duration = ref(0)
    const progress = ref(0)
    const volume = ref(100)

    // 定义命名事件处理函数用于正确移除监听
    const handleTimeUpdate = () => {
        const audio = audioRef.value
        if (!audio) return
        currentTime.value = audio.currentTime
        if (audio.duration > 0) { // 防止duration未加载完成时计算错误
            progress.value = (audio.currentTime / audio.duration) * 100
        }
    }

    const handleLoadedMetadata = () => {
        const audio = audioRef.value
        if (audio) duration.value = audio.duration
    }

    const handleEnded = () => {
        isPlaying.value = false
    }

    const handleError = (e) => {
        console.error('音频加载失败:', e)
        // 可根据业务需求添加提示(如使用ElMessage)
        // ElMessage.error('音频加载失败,请检查地址')
    }

    onMounted(() => {
        const audio = audioRef.value
        if (!audio) return
        
        audio.addEventListener('timeupdate', handleTimeUpdate)
        audio.addEventListener('loadedmetadata', handleLoadedMetadata)
        audio.addEventListener('ended', handleEnded)
        audio.addEventListener('error', handleError)
    })

    onBeforeUnmount(() => {
        const audio = audioRef.value
        if (!audio) return
        
        audio.removeEventListener('timeupdate', handleTimeUpdate)
        audio.removeEventListener('loadedmetadata', handleLoadedMetadata)
        audio.removeEventListener('ended', handleEnded)
        audio.removeEventListener('error', handleError)
        audio.pause() // 卸载时暂停播放
    })

    const togglePlay = () => {
        const audio = audioRef.value
        if (!audio) return
        
        if (isPlaying.value) {
            audio.pause()
        } else {
            audio.play().catch(err => {
                console.error('播放失败:', err)
                isPlaying.value = false // 播放失败时恢复状态
            })
        }
        isPlaying.value = !isPlaying.value
    }

    const handleProgressChange = (value) => {
        const audio = audioRef.value
        if (!audio || audio.duration === 0) return // 防止未加载完成时操作
        
        const time = (value / 100) * audio.duration
        audio.currentTime = time
        currentTime.value = time
    }

    const handleVolumeChange = (value) => {
        const audio = audioRef.value
        if (audio) audio.volume = value / 100
    }

    const formatTime = (time) => {
        const minutes = Math.floor(time / 60)
        const seconds = Math.floor(time % 60)
        return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
    }
    const onCanplay = () => {
        if (audioRef.value) {
            duration.value = audioRef.value.duration
        }
    }
    // 关闭后重置并关闭播放
    const reset = () => {
        isPlaying.value = false
        currentTime.value = 0
        progress.value = 0 
        if (audioRef.value) {
            audioRef.value.pause() 
            audioRef.value.currentTime = 0 
            audioRef.value.src = props.audioSrc 
            audioRef.value.load() 
        }
    }
    defineExpose({ reset })
</script>

css

<style lang="scss" scoped>
    .my-video-group{
        border-radius: 50px;
        padding: 4px;
        background: var(--appmain-default-bg);
    }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值