记录自己第一次实现音频波纹+文字气泡

该代码段展示了如何结合WaveSurfer.js库创建音频波纹效果,并在音频波形上添加文字气泡。通过监听音频加载,初始化波纹图,然后在特定时间点绘制文字气泡。同时,实现了音频播放、暂停、速度控制等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

音频波纹+文字气泡

<div class="box">
	<div class="wave-bubble-content">
         <div id="waveform" @click="getTime">
             <canvas
                 id="wave-bubble-text"
                 class="wave-bubble-text"
             ></canvas>
         </div>
     </div>
     <record-audio-player
         v-if="audioUrl"
         ref="audioPlayer"
         class="audio-player"
         :src="audioUrl"
         name="音频名称"
         @setCurrentTime="changeCurrentTime"
         @setSpeed="onSetSpeed"
         @ended="onEnded"
         @pause="onPause"
         @play="onPlay"
     />
</div>
methods: {
    async getDetails() {
        this.audioDetails = await this.$store.dispatch('audioAnalysis/one', {id: this.audioId});
        const params = {
            key: this.audioDetails.fileKey
        };
        const url = await this.getSoundUrl({params});
        this.audioUrl = url;
        const audioElement = new Audio(this.audioUrl);
        audioElement.addEventListener('loadedmetadata', () => {
            this.totalTime = audioElement.duration;
            // 音频加载成功后
            if (this.audioUrl) {
                this.initWave();
                const textCanvas = document.querySelector('#wave-bubble-text');
                this.addMark(textCanvas);
            }
        });
    },
    // 设置文字气泡的位置
    addMark(container) {
        const canvas = document.createElement('canvas');
        container.appendChild(canvas);
        const canvasWidth = 3600;

        // 画一个超大的canvas 否则字会很模糊
        canvas.width = canvasWidth;
        canvas.height = 360;
        canvas.style.display = 'none';

        const cansContext = canvas.getContext('2d');
        cansContext.font = '30px Microsoft YaHei';
        cansContext.textAlign = 'left';
        this.resultList.map(item => {
            // 文字最左边的的横坐标
            const x = timeToSeconds(item.time) / this.totalTime * canvasWidth;
            // 文字最上面的纵坐标
            const y = 70;
            // 文字像素大小 30px
            const fSize = 30;
            // 30px对应的字体高度 40px
            const Yh = 40;
            // 绘制气泡
            cansContext.beginPath();
            cansContext.moveTo(x, y);
            cansContext.lineTo(x + item.text.length * fSize + 20, y);
            cansContext.lineTo(x + item.text.length * fSize + 20, y + Yh);
            cansContext.lineTo(x + 30, y + Yh);
            cansContext.lineTo(x + 20, y + Yh + 15);
            cansContext.lineTo(x + 10, y + Yh);
            cansContext.lineTo(x, y + Yh);
            cansContext.lineTo(x, y);
            cansContext.closePath();
            cansContext.stroke();
            cansContext.fillStyle = '#2468f2';
            cansContext.fill();
            // 绘制文字
            cansContext.fillStyle = 'white';
            cansContext.fillText(item.text, x + 10, y + Yh - 10);
        });
        container.style.backgroundImage = 'url(' + canvas.toDataURL('image/png') + ')';
        container.style.backgroundSize = '100%';
        container.style.backgroundRepeat = 'no-repeat';
    },
    getSoundUrl(options) {
        return customizeRequest.instance({
            method: 'get',
            url: options.url || apis.url.fileDownload,
            params: {
                ...options.params,
                getParent: false
            },
            headers: {
                'Authorization': getAuthorization(options),
            },
            responseType: 'blob',
        }).then(res => {
            if (!res.headers['content-disposition']) {
                return console.error('文件名称不存在');
            }
            // 获取到的blob类型文件地址
            return URL.createObjectURL(res.data);
        }).catch(err => {
            console.error('文件获取失败');
        });
    },
    initWave() {
        // 官网文档地址 https://wavesurfer-js.org/docs/
        this.wavesurfer = WaveSurfer.create({
            // 波形图的容器
            container: '#waveform',
            // 已播放波形的颜色
            progressColor: 'orange',
            // 未播放波形的颜色
            waveColor: 'green',
            // 波形图的高度,单位为px
            height: 120,
            // 波形的振幅(高度),默认为1 ,总时长大于200s的稍微把波形振幅提高一点
            barHeight: this.totalTime > 200 ? 0.6 : 0.4,
            // 波形条的圆角
            barRadius: 2,
            // 波形条的宽度
            barWidth: 1,
            // 波形条间的间距
            barGap: 3,
            // 播放进度光标条的颜色
            cursorColor: 'black',
            // 播放进度光标条的宽度,默认为1
            cursorWidth: 1,
            //  波形容器的背景颜色
            backgroundColor: 'white',
            // 音频的播放速度
            audioRate: '1',
            // (与区域插件一起使用)启用所选区域的循环
            loopSelection: false
        });
        // 这里需要后端支持cors 否则跨域 不过这种获取到的blob类型文件地址貌似就不跨域了
        this.wavesurfer.load(this.audioUrl);
        // 静音
        this.wavesurfer.setMute(true);
    },
    // 点击获取点击进度
    getTime() {
        setTimeout(() => {
            const currentTime = this.wavesurfer.getCurrentTime();
            const audio = this.$refs.audioPlayer;
            audio.setCurrentTime(currentTime);
            // 点击波纹 会让audio进度条对应跳转,但会导致触发changeCurrentTime事件,因此用play方法覆盖,解决展示错误问题
            // 让他看上去像是暂停一样,直接点波纹就是会播放,但是pm希望不播放,如果给pause,则光标展示的地方不对,目前只能暂时这么解决
            this.wavesurfer.play(currentTime, currentTime + (1 / 100));
        }, 100);
    },
    // 点击audio进度条事 设置wavesurfer对应的当前时间
    changeCurrentTime() {
        // 点击 audio 进度条的时候,让波纹同步的跳转到对应时间点
        const currentTime = this.$refs.audioPlayer.currentTime;
        const oldTime = this.wavesurfer.getCurrentTime();
        this.wavesurfer.skip(currentTime - oldTime);
    },
    // 设置wavesurfer的播放速度
    onSetSpeed(speed) {
        this.wavesurfer.setPlaybackRate(speed);
    },
    onEnded() {
        clearInterval(this.videoInterval);
    },
    // 暂停
    onPause() {
        clearInterval(this.videoInterval);
        this.wavesurfer.pause();
    },
    // audio 按钮点击事件
    onPlay() {
        const currentTime = this.$refs.audioPlayer.$refs.audio.currentTime;
        this.wavesurfer.play(currentTime);
        let timer = parseInt(currentTime, 10);
        this.videoInterval = setInterval(() => {
            this.$refs.sessionBubble.moveToScreenView(timer);
            timer++;
        }, 1000);
    },
    handlePageChange(val) {
        this.page = val;
        this.getRecordDetail();
    },
    sizeChange(val) {
        this.pageSize = val;
        this.page = 1;
        this.getRecordDetail();
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值