audio自定义组件

234 阅读1分钟

对于音频标签无法自定义如何解决: 样式: image.png

image.png 功能:

倍数播放,可拖拽进度条

引用方法:

import cnAudio from './cn-audio';
<cnAudio :audioUrl='音频文件'/>

组件内容:
[代码片段](https://code.juejin.cn/pen/7121921287358234654)<-- html -->
<template>
    <div class="add-summary-audio-new" :class="{'audio-box' : audioPlay === '1'}">
        <div class="audio-control">
            <div class="audio-play-control" @click="changeAudioPlay">
                <i class="iconfont" v-if="audioPlay === '1'">&#xe67f;</i>
                <i class="iconfont" v-else>&#xe680;</i>
            </div>
        </div>
        <div class="audio-download">
            <i class="audio-download-i"> {{ audio.currentTimeText }} / </i>
            <i class="audio-download-i">
                {{ audio.totalTimeText }}
            </i>
        </div>
        <div class="audio-progress">
            <el-slider
                v-model="progress"
                :step="0.01"
                :min="0"
                :max="audio.duration"
                @change="progressChange"
                :format-tooltip="formatTime"
            ></el-slider>
        </div>

        <div class="audio-volume">
            <div class="volume-controls" v-if="volumeStatus!=='0'">
                <el-slider v-model="volume" @change="volumeChange"></el-slider>
            </div>
            <div class="volume-icon">
                <i
                    class="iconfont"
                    v-if="volumeStatus === '0'"
                    @click="volumeTypeChange('1')"
                    >&#xe6fd;</i
                >
                <i class="iconfont" v-else @click="volumeTypeChange('0')"
                    >&#xe700;</i
                >
            </div>
        </div>
        <el-dropdown class="audio-info" @command="handleCommandMore">
            <div class="foot-msg-right" @click="showSpeed = true">
                <i class="iconfont foot-msg-right icon-PTgengduo"></i>
            </div>
            <div
                class="audio-speed-popover-bg"
                v-if="showSpeed"
                @click="showSpeed = false"
            ></div>
            <el-dropdown-menu slot="dropdown">
                <el-dropdown-item
                    v-for="item in speedOptions"
                    :key="item.value"
                    :class="[
                        'audio-speed-items',
                        { actived: currentSpeed === item.value },
                    ]"
                    :command="{ data: item }"
                    >{{ item.label }}</el-dropdown-item
                >
            </el-dropdown-menu>
        </el-dropdown>

        <audio
            ref="audio"
            style="width: 0; height: 0; overflow: hidden"
            controls
            preload="metadata"
            :src="audioUrl"
            @loadedmetadata="loadedmetadata"
            @canplay="canplay"
            @ended="ended"
            @timeupdate="timeupdate"
        ></audio>
    </div>
</template>

<-- vue -->
<script>
export default {
    name: "add-summary-audio-new",
    props: {
        audioUrl: {
            type: String,
            required: true,
        },
    },
    data() {
        return {
            // 播放进度
            progress: 0,
            // 播放速度
            currentSpeed: '4',
            speedOptions: [
                { value: "1", label: "2x" },
                { value: "2", label: "1.75x" },
                { value: "3", label: "1.25x" },
                { value: "4", label: "1x" },
                { value: "5", label: "0.75x" },
                { value: "6", label: "0.5x" },
            ],
            showSpeed: false,
            // 音量状态,关闭(0),开启(1)
            volumeStatus: "1",
            // 音量百分比
            volume: 50,
            // 播放状态,播放(1),暂停(0)
            audioPlay: "0",
            audio: {
                // 是否准备就绪
                isReady: false,
                duration: 0,
                currentTimeText: "00:00",
                totalTimeText: "",
            },
            progressValue: 0,
        };
    },
    computed: {
        speedValues() {
            const s = this.speedOptions;
            const data = {};
            for (let i = 0; i < s.length; i++) {
                data[s[i].value] = parseFloat(s[i].label);
            }
            return data;
        },
    },
    methods: {
        throttle(fn, delay = 1000) {
            let timer = null;
            return function () {
                let context = this;
                let args = arguments;

                if (!timer) {
                    fn.apply(context, args);
                    timer = setTimeout(function () {
                        timer = null;
                    }, delay);
                }
            }[代码片段](https://code.juejin.cn/pen/7121921287358234654)
        },
        handleCommandMore(params) {
            if (this.audio.isReady) {
                this.currentSpeed = params.data.value;
                this.$refs.audio.playbackRate = this.speedValues[
                    params.data.value
                ];
            }
        },
        volumeTypeChange(val) {
            console.log(val);
            if (!this.audio.isReady) return;
            this.volumeStatus = val;
            if (val === "0") {
                this.$refs.audio.muted = true;
            } else {
                this.$refs.audio.muted = false;
            }
        },
        volumeChange(val) {
            console.log(val);
            this.volume = val;
            if (this.audio.isReady) {
                this.$refs.audio.volume = val / 100;
            }
        },
        changeAudioPlay() {
            const res = ["1", "0"];
            if (this.audio.isReady) {
                this.audioPlay = res[this.audioPlay];
                if (this.audioPlay === "0") {
                    this.$refs.audio.pause();
                } else {
                    this.$refs.audio.play();
                }
            }
        },
        loadedmetadata(e) {
            this.audio.duration = e.target.duration;
            this.audio.totalTimeText = this.formatTime(e.target.duration);
            this.$refs.audio.volume = 0.5;
        },
        canplay() {
            this.audio.isReady = true;
        },
        // 播放完成自动停止
        ended() {
            this.audioPlay = "0";
        },
        timeupdate(e) {
            this.audio.currentTimeText = this.formatTime(e.target.currentTime);
            this.$nextTick(() => {
                this.progress = e.target.currentTime;
                // this.progress = (100 * e.target.currentTime / this.audio.duration).toFixed(2) / 1;
            });
        },
        progressChange(value) {
            // console.log("当前进度" + value)
            this.$refs.audio.currentTime = value;
            // this.$refs.audio.currentTime = (value / 100) * this.audio.duration;
        },

        formatTime(value) {
            if (!value || !parseInt(value)) return "00:00";

            const second = Math.floor(value % 60);
            const minite = Math.floor(value / 60);
            return `${minite}:${second >= 10 ? second : `0${second}`}`;
        },
    },
};
</script>
<-- css -->
<style lang="less" scoped>
.add-summary-audio-new {
    display: flex;
    height: 36px;
    line-height: 36px;
    background: #f8f9fa;
    border-radius: 51px;
}
.audio-box {
    background: rgba(247, 106, 43, 0.08);
}

.audio-progress {
    width: 100%;
    padding: 0 10px;
    margin: 5px 0;
    /deep/ .el-slider__button {
        width: 12px;
        height: 12px;
        background-color: #f76a2b;
        border: 2px solid #ffffff;
    }
    /deep/ .el-slider__runway {
        margin-top: 10px;
        background: #d0d3d6;
    }
}
.audio-play-control {
    .iconfont {
        cursor: pointer;
    }
}
.audio-download {
    margin: 0 6px;
    min-width: 68px;
    .audio-download-i {
        font-size: 12px;
        color: #262626;
        font-style: normal;
    }
}
.audio-progress {
    margin-right: 12px;
}
.audio-progress:hover{
    background-color: rgba(247, 106, 43, 0.1);
    border-radius: 51px;
}
.volume-controls {
    margin-right: 6px;
    .el-slider__bar {
        transform: rotate(90deg);
    }
    /deep/ .el-slider__runway{
        margin-top: 10px;
    }
    /deep/ .el-slider__button {
        width: 0;
        height: 0;
        border: none;
    }
}
.audio-info {
    margin: 0 6px;
}
.audio-volume {
    display: flex;
    justify-content: space-around;
}

.audio-volume {
    padding: 0 10px;
    margin:5px  0;
    line-height: 26px;
    .volume-controls {
        width: 0px;
        // height: 50px;
        transition: width 0.2s ease 0.2s;
    }
    
}
.audio-volume:hover {
    background-color: rgba(247, 106, 43, 0.1);
    border-radius: 51px;
    .volume-controls {
        width: 80px;
    }
    /deep/ .el-slider__button {
        width: 12px;
        height: 12px;
        background-color: #f76a2b;
        border: 2px solid #ffffff;
    }
}
.audio-speed-items {
    font-size: 14px;
    text-align: center;
    cursor: pointer;
    &.actived {
        color: #F76A2B;
    }
}
</style>