使用uniapp + Vue3 + uni.createInnerAudioContext()实现播放歌曲及歌词滚动、拖动进度条

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

前言

1、本文记录一下自己在学习uniapp中遇到的问题,在使用ref获取dom节点时发现并不可以于是换用css实现歌词的滚动。

一、大致效果

二、使用步骤

1.歌词详情页代码块

代码如下(示例):

<!-- 歌词详情界面 -->
<template>
    <view class="music-filter" :style="{backgroundImage:'url('+usePlayer.playerList.song.imgurl+')'}" />
    <view class="music-detail">
        <view class="detail-header">
            <u-icon name="arrow-left" class="arrow-left" @click="goBack(1)" color="#707070" size="22" />
            <text>{{usePlayer.playerList.song.name}}({{usePlayer.playerList.song.author}})</text>
        </view>
        <view class="music-lyc mar-b-24" ref="musicLyc" :style="{top:top+'px'}">
            <view :style="{transform:'translateY('+ height + 'px)'}">
                <view class="lyc-name">歌曲 : {{usePlayer.playerList.song.name}}</view>
                <view class="lyc-author">
                    <text>歌手 : {{usePlayer.playerList.song.author}}</text>
                </view>
                <view class="lyc">
                    <view class="over" v-for="(lyc,index) in lycArr" :key="index" :class="{active:(usePlayer.playerList.musicCurrentTime *1000>=lyc.time && 
                        usePlayer.playerList.musicCurrentTime *1000<lyc.pre)}">
                        {{lyc.lrc}}
                    </view>
                </view>
            </view>
        </view>
        <!-- 进度条 -->
        <view class="progress">
            <slider min="0" :max="usePlayer.playerList.maxTime" block-size="12" activeColor="#000"
                backgroundColor="#999" :value="usePlayer.playerList.percentage" @change="move" />
            <view class="progress-time">
                <view>{{getMusicTime(usePlayer.playerList.musicCurrentTime)}}</view>
                <view>{{getMusicTime(usePlayer.playerList.musicEndTime)}}</view>
            </view>
        </view>
        <view class="music-operation">
            <view @click="usePlayer.playLoopMusic(0)" v-if="!usePlayer.playerList.loop"
                class="t-icon t-icon-bofang-xunhuanbofang" />
            <view @click="usePlayer.playLoopMusic(1)" class="t-icon-suijibofang t-icon" v-else />
            <view @click="playTab('pre')" class="t-icon t-icon-bofang-shangyige" />
            <view @click="changeStaus(0)" class="t-icon-plays t-icon-ziyuan" v-if="usePlayer.playerList.isPlay" />
            <view @click="changeStaus(1)" class="t-icon-plays t-icon-zanting" v-else />
            <view @click="playTab('next')" class="t-icon t-icon-bofang-xiayige" />
            <view class="t-icon t-icon-shunxubofang" />
        </view>
    </view>
</template>
<script setup>
    import {
        ref,
        onMounted,
        onBeforeMount,
        watch
    } from 'vue'
    import {
        getSongUrl
    } from '@/api/songList.js'
    import {
        onLoad
    } from "@dcloudio/uni-app"
    import {
        goBack,
        getMusicTime
    } from '@/utils/hook.js'
    import {
        usePlayerStore
    } from '@/store/index.js'
    const usePlayer = usePlayerStore()
    const currentTime = ref(0)
    const lycArr = ref([])
    // 处理歌词
    const lyric = () => {
        let arr
        if (usePlayer.playerList.song.lyc) {
            arr = usePlayer.playerList.song.lyc.split(/[(\r\n)\r\n]+/).map((item, i) => {
                let min = item.slice(1, 3)
                let sec = item.slice(4, 6)
                let mill = item.slice(7, 10)
                let lrc = item.slice(11, item.length)
                let time = parseInt(min) * 60 * 1000 + parseInt(sec) * 1000 + parseInt(mill)
                if (isNaN(Number(mill))) {
                    mill = item.slice(7, 9)
                    lrc = item.slice(10, item.length)
                    time = parseInt(min) * 60 * 1000 + parseInt(sec) * 1000 + parseInt(mill)
                }
                return {
                    min,
                    sec,
                    mill,
                    lrc,
                    time
                }
            })
        }
        if (arr) {
            arr.forEach((item, i) => {
                if (i === arr.length - 1 || isNaN(arr[i + 1].time)) {
                    item.pre = 100000
                } else {
                    item.pre = arr[i + 1].time
                }
            });
        }
        return arr
    }
    onBeforeMount(() => {
        if (!usePlayer.playerList.isPlay) {
            getMusicUrls()
        }
        lycArr.value = lyric()
    })
    // 暂停或开始音乐
    const changeStaus = (type) => {
        if (!type) usePlayer.innerAudioContext.pause()
        else usePlayer.innerAudioContext.play()
        usePlayer.setIsPlay(!usePlayer.playerList.isPlay)
    }
    // 拖动滚动条
    const move = (e) => usePlayer.setSeekMusic(e.detail.value)
    // 切换歌曲
    const playTab = (type) => {
        if (type === 'pre') {
            // 如果当前索引为0点击上一首切换到最后一首
            if (usePlayer.playerList.musicIndex === 0) {
                usePlayer.setMusicIndex(usePlayer.playerList.musicList.length - 1)
            } else usePlayer.setMusicIndex(usePlayer.playerList.musicIndex - 1)
        } else {
            // 如果当前索引为最后一位点击上一首切换到第一首
            if (usePlayer.playerList.musicIndex === usePlayer.playerList.musicList.length - 1) {
                usePlayer.setMusicIndex(0)
            } else usePlayer.setMusicIndex(usePlayer.playerList.musicIndex + 1)
        }
        getMusicUrls()
    }
    // 获取歌曲url及歌词
    const getMusicUrls = async () => {
        let id = usePlayer.playerList.song.id
        usePlayer.getMusicUrl(id)
        lycArr.value = lyric()
    }
    // 监听歌曲是否改变
    watch(() => usePlayer.playerList.musicIndex, (newValue, oldValue) => {
        if (newValue !== oldValue && !usePlayer.playerList.isPlay)
            getMusicUrls()
    })
    // 实现歌词滚动
    const height = ref(0)
    const top = ref(0)
    watch(() => usePlayer.playerList.musicCurrentTime, () => {
        height.value = top.value = 0
        let p = document.querySelector('.active')
        if (p) {
            if (p.offsetTop > 330) {
                height.value = top.value = 330 - p.offsetTop
            }
        }
    })
</script>

<style lang="scss">
    .music-filter {
        position: absolute;
        z-index: -1;
        height: 100vh;
        width: 100%;
        top: 70vh;
        background-color: #fff;
        filter: blur(200rpx);
        background-repeat: no-repeat;
        background-size: 350% 100%;
        position: fixed;
    }

    .music-detail {
        height: 100vh;
        width: 100%;
        padding: 24rpx;
        box-sizing: border-box;
        display: flex;
        flex-direction: column;
        align-items: center;
        position: fixed;

        .detail-header {
            height: 40rpx;
            width: 100%;
            display: flex;
            align-items: center;
            color: #707070;

            text {
                margin: 0 auto;
            }
        }

        .music-lyc {
            margin-top: 40rpx;
            overflow-y: auto;
            text-align: center;
            height: 80vh;
            width: 80%;

            .lyc-name {
                font-size: 16px;
                color: #707070;
                margin-bottom: 10rpx;
                padding: 10rpx;
            }

            .lyc-author {
                font-size: 14px;
                color: #A3A3A3;
                padding: 10rpx;
                margin-bottom: 10rpx;
            }

            .lyc {
                width: 100%;
                height: 80%;

                view {
                    width: 100%;
                    padding: 10rpx;
                    color: #767676;
                    font-size: 14px;
                    text-align: center;
                }

                .active {
                    font-size: 22px;
                    color: #000
                }
            }
        }

        .progress {
            width: 100%;
            display: flex;
            flex-direction: column;

            .progress-time {
                width: 100%;
                display: flex;
                align-items: center;
                justify-content: space-between;

                view {
                    padding: 10rpx;
                }
            }
        }

        .music-operation {
            margin: 40rpx;
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 15rpx;

            .t-icon {
                width: 50rpx;
                height: 50rpx;
            }

            .t-icon-plays {
                width: 85rpx;
                height: 85rpx;
            }
        }
    }
</style>
​​​​​​​


2.封装到仓库(pinia)的音乐实例

代码如下(示例):

// 导入定义仓库的方法
import {
	defineStore
} from 'pinia';
import {
	getSongUrl
} from '@/api/songList.js'
// 导入响应式和计算
import {
	ref
} from 'vue'
export const usePlayerStore = defineStore("player", () => {
	// 全局音乐实例
	const innerAudioContext = ref(null)
	// 歌曲信息
	const playerList = ref({
		isPlay: false,
		song: {
			"lyc": "[00:00.00] 作词 : Savan Kotecha/Peter Svensson/Ilya/Ariana Grande\n[00:01.00] 作曲 : Savan Kotecha/Peter Svensson/Ilya/Ariana Grande\n[00:04.51]Hey\n[00:09.53]I know what I came to do\n[00:11.86]And that ain't gonna change\n[00:14.27]So go ahead and talk your talk\n[00:16.63]Cuz I won't take the bait\n[00:19.10]I'm over here doing what I like\n[00:21.43]I'm over here working day and night\n[00:23.86]If my real ain't real enough\n[00:26.31]I'm sorry for you bae\n[00:28.62]Let's find a light inside our universe now\n[00:33.13]Where ain't nobody keep on holding us down\n[00:37.90]Just come and get it let them say what they say\n[00:42.78]Cuz I'm about to put them all away\n[00:48.15]Focus on me, f-f-focus on me\n[00:52.68]Focus on me, f-f-focus on me\n[00:57.55]Focus on me (Focus), f-f-focus on me (Focus on me)\n[01:02.34]Focus on me (Focus), f-f-focus on me (Focus on me)\n[01:06.80]I can tell you're curious\n[01:09.12]It's written on your lips\n[01:11.81]Ain't no need to hold it back\n[01:14.29]Go head and talk your ****\n[01:16.72]I know you're hoping that I'll react\n[01:19.11]I know you're hoping I'm looking back\n[01:21.52]But if my real ain't real enough\n[01:23.82]Then I don't know what is\n[01:26.28]Let's find a light inside our universe now\n[01:30.87]Where ain't nobody keep on holding us down\n[01:35.53]Just come and get it let them say what they say\n[01:40.34]Cuz I'm about to put them all away\n[01:45.39]Focus on me, f-f-focus on me\n[01:50.14]Focus on me, f-f-focus on me\n[01:54.98]Focus on me (Focus), f-f-focus on me (Focus on me)\n[01:59.78]Focus on me (Focus), f-f-focus on me (Focus on me)\n[02:04.67]1, 2, 3, c'mon girls\n[02:10.80]You're gonna like it\n[02:18.26]Come on, now, now\n[02:22.64]\n[02:26.21]Let's find a light inside our universe now\n[02:30.45]Where ain't nobody keep on holding us down\n[02:35.29]Just come and get it let them say what they say\n[02:40.03]Cuz I'm about to put them all away\n[02:47.54]Focus on me, f-f-focus on me\n[02:52.27]Focus on me, f-f-focus on me\n[02:57.04]Focus on me (Focus), f-f-focus on me (Focus on me)\n[03:01.79]Focus on me (Focus), f-f-focus on me (Focus on me)\n[03:06.66]Focus on me, f-f-focus on me\n[03:11.37]Focus on me, f-f-focus on me\n[03:16.12]Focus on me (Focus), f-f-focus on me (Focus on me)\n[03:20.98]Focus on me (Focus), f-f-focus on me (Focus on me)\n",
			"songUrl": "http://m702.music.126.net/20231231172931/0e4c69f75f09f72910e736fe5f7526ef/jd-musicrep-ts/6a09/2688/9ba3/8dcfa2324f4a28a11e3f83bc087ce549.mp3",
			"singerId": 48161,
			"id": 36025888,
			"name": "Focus",
			"imgurl": "https://p2.music.126.net/lFflM8_fYYBzr4YYCmdX3A==/109951166304034425.jpg",
			"author": "Ariana Grande"
		},
		musicIndex: 0,//歌曲索引值
		musicList: [],//歌单数组
		musicCurrentTime: 0,//歌曲开始时间
		musicEndTime: 0,//歌曲总时长
		percentage: 0,//歌曲当前时间
		loop: false,//循环播放
		maxTime: 100,//进度条最大值
	})
	// 存放歌曲数组
	const setMusicList = (arr) => {
		playerList.value.musicList = arr
	}
	// 更新音乐索引index及歌曲信息
	const setMusicIndex = (index) => {
		playerList.value.musicIndex = index
		const { id, author, name, imgurl, lyc } = playerList.value.musicList[index]
		playerList.value.song.id = id
		playerList.value.song.name = name
		playerList.value.song.author = author
		playerList.value.song.imgurl = imgurl
		playerList.value.song.lyc = lyc
		console.log(index, playerList.value.song)
	}
	// 播放或暂停音乐
	const setIsPlay = (val) => {
		playerList.value.isPlay = val
	}
	//更新歌曲时间
	const setMusicTime = (type, time) => {
		if (type === 'start') {
			playerList.value.musicCurrentTime = time
		} else {
			playerList.value.musicEndTime = time
		}
	}
	// 获取歌曲url及歌词
	const getMusicUrl = async (id) => {
		let res = await getSongUrl(playerList.value.song.id)
		playerList.value.song.songUrl = res.data[0].url
		if (innerAudioContext.value) innerAudioContext.value.destroy()
		creatAudio(res.data[0].url)
	}
	// 创建音乐实例
	const creatAudio = (url) => {
		innerAudioContext.value = uni.createInnerAudioContext() //创建音乐实例
		innerAudioContext.value.autoplay = true //设置是否自动播放
		innerAudioContext.value.src = url //音频的url
		innerAudioContext.value.onPlay(() => {
			// 播放监听
			setIsPlay(true)
		});
		innerAudioContext.value.onPause(() => {
			// 暂停监听
			setIsPlay(false)
		});
		innerAudioContext.value.onEnded(() => {
			// 结束播放监听
			setIsPlay(false)
			//自动切换事件
			if (playerList.value.musicIndex === playerList.value.musicList.length - 1) setMusicIndex(0)
			else setMusicIndex(playerList.value.musicIndex + 1)
		});
		innerAudioContext.value.onTimeUpdate(() => {
			const {
				currentTime,
				duration
			} = innerAudioContext.value;
			playerList.value.percentage = parseInt(currentTime)
			playerList.value.maxTime = parseInt(duration)
			setMusicTime('start', currentTime)
			setMusicTime('end', duration)
		});
	}
	// 循环播放音乐
	const playLoopMusic = (val) => {
		if (!val) innerAudioContext.value.loop = playerList.value.loop = true
		else {
			innerAudioContext.value.loop = playerList.value.loop = false
			let index = Math.floor(Math.random() * playerList.value.musicList.length)
		}
	}
	// 拖动音乐
	const setSeekMusic = (val) => {
		playerList.value.percentage = parseInt(val)
		setMusicTime('start', val)
		innerAudioContext.value.seek(val)
		innerAudioContext.value.play()
	}
	// 导出
	return {
		playerList,
		setIsPlay,
		setMusicList,
		setMusicIndex,
		setMusicTime,
		creatAudio,
		innerAudioContext,
		playLoopMusic,
		setSeekMusic,
		getMusicUrl
	}
})

总结

本文只做学习记录使用,有任何不对的地方欢迎大佬指正。转载请备注出处。

  • 13
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: uni.createInnerAudioContext是一个Uni-app框架中的API,用于创建一个内部音频上下文对象。该对象可以用于播放音频文件,控制音频播放状态等。使用该API需要先引入uni-app的API模块。 ### 回答2: uni.createInnerAudioContext是一个创建内部音频上下文的API,该API用于在uni-app中播放本地音频文件。它是一个全局方法,可以在任何地方调用。 该方法会返回一个InnerAudioContext对象,可以用来控制音频的播放、暂停、停止、音量控制、进度控制等功能。同时该对象还可以获取音频的信息,例如当前的播放时间、总时长、播放状态等。 在创建InnerAudioContext对象时,可以通过设置src属性来设置要播放的音频文件的路径。此外,InnerAudioContext还提供了一些事件回调函数,例如onPlay、onPause、onStop、onEnded等,可以根据不同的事件来触发不同的操作。 InnerAudioContext对象可以在uni-app中播放多个音频文件,也可以同时控制多个音频的播放、暂停等。同时,InnerAudioContext还可以设置循环播放,即音频播放完后自动重新播放,以及设置是否自动播放等功能。 总之,uni.createInnerAudioContext是一个非常有用的API,它可以帮助开发者在uni-app实现简单的音频播放功能,使得uni-app开发更加完善。 ### 回答3: uni.createInnerAudioContext 是在uni-app中创建音频资源实例的方法。在实际开发中,我们通常需要使用音频播放、录音、转码等功能,这就需要用到 uni.createInnerAudioContext 这个方法。 uni.createInnerAudioContext 可以创建一个含有音频资源信息的 audioContext 对象。我们可以通过该对象的 play()、pause()、seek() 等方法来控制音频的播放和暂停。 通过 uni.createInnerAudioContext 创建的 audioContext 对象支持的属性有如下: src: 音频的资源地址 startTime: 开始播放的位置(单位 s) autoplay: 是否自动播放 loop: 是否循环播放 obeyMuteSwitch: 是否遵循系统静音开关 volume: 音量大小,范围是0至1之间 currentTime: 当前播放时间 duration: 音频总时长 通过这些属性和方法,我们可以在适当的时候调整音频的播放状态,实现更加自然且符合用户需求的音频播放方式。 总之,uni.createInnerAudioContext 可以说是 uni-app 中非常重要的音频播放相关方法之一,它可以让我们更加便捷、灵活地管理音频资源,为用户带来更好的使用体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值