好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Vue3实现简易音乐播放器组件

前言

用Vue3实现一个简易的音乐播放器组件

其效果图如下所示:

实现这个组件需要提前做的准备:

引入ElementUI 引入字节跳动图标库 一张唱见图片 将要播放的音乐上传到文件服务器上,并提供一个能在线访问的链接【这里使用的是阿里云的OSS服务】

准备

ElementUI

ElementUI的引入可以参照其官网的引入方式;

字节跳动图标库

组件的【上一首】【播放】【下一首】【音量】等图标都是来源自这个图标库,这是其 安装文档

在main.js中,我是这样引入的:

//引入字节跳动图标库
import {install} from '@icon-park/vue-next/es/all';
import '@icon-park/vue-next/styles/index.css';

......

//这种加载方式进行加载的话,代表使用默认的前缀进行加载:icon
//也就是说假如要使用一个主页图标,使用图标时标签该这么写:?
//<icon-home theme="outline" size="24" fill="#FFFFFF" :strokeWidth="2"/>
//install(app,'prefix') 用这种方式进行加载的话,可以自定义使用图标库时的标签前缀
install(app)

唱见图片

音乐源

将要播放的音乐放到文件服务器上,我这里是使用阿里云的OSS服务进行音乐文件的存储,然后在整个页面加载时【也就是在onMounted生命周期函数中获取这些数据源】。在后面的代码中,这一步体现在:

//初始化歌曲源
const initMusicArr = () => {
? ? ? ? requests.get("/Music/QueryAllMusic").then(function (res) {
? ? ? ? ? ? musicState.musicArr = res

? ? ? ? ? ? musicState.musicCount = res.length
? ? ? ? })
? ? }

? ? onMounted(() => {
? ? ? ? initMusicArr()

? ? ? ? ? ? ......
? ? })

完整代码

<template>

? <!--音乐播放器-->
? <div class="music-container" :class="{'music-active-switch': offsetThreshold}">
? ? <div class="music-disk">
? ? ? <!--唱片图片-->
? ? ? <img class="music-disk-picture" :class="{'music-disk-playing-style': playState}" src="./images/R-C.png"
? ? ? ? ? ?alt="">
? ? </div>

? ? <!--进度条-->
? ? <div class="music-slider">
? ? ? <el-slider
? ? ? ? ? v-model="playTime"
? ? ? ? ? :format-tooltip="tooltipFormat"
? ? ? ? ? size="small"
? ? ? ? ? :max="sliderLength"
? ? ? ? ? @change="changePlayTime"/>
? ? </div>

? ? <!--按钮组-->
? ? <div class="button-group">
? ? ? <!--上一曲 按钮-->
? ? ? <button class="play-button" @click="lastButtonClick">
? ? ? ? <icon-go-start theme="outline" size="23" fill="#939393" :strokeWidth="3" strokeLinejoin="miter"
? ? ? ? ? ? ? ? ? ? ? ?strokeLinecap="butt"/>
? ? ? </button>
? ? ? <!--播放 按钮-->
? ? ? <button class="play-button" @click="playButtonClick">
? ? ? ? <icon-play-one v-if="!playState" theme="outline" size="23" fill="#939393" :strokeWidth="3"
? ? ? ? ? ? ? ? ? ? ? ?strokeLinejoin="miter" strokeLinecap="butt"/>
? ? ? ? <icon-pause v-if="playState" theme="outline" size="23" fill="#939393" :strokeWidth="3"
? ? ? ? ? ? ? ? ? ? strokeLinejoin="miter" strokeLinecap="butt"/>
? ? ? </button>
? ? ? <!--下一曲 按钮-->
? ? ? <button class="play-button" @click="nextButtonClick">
? ? ? ? <icon-go-end theme="outline" size="23" fill="#939393" :strokeWidth="3" strokeLinejoin="miter"
? ? ? ? ? ? ? ? ? ? ?strokeLinecap="butt"/>
? ? ? </button>
? ? ? <!--音量按钮-->
? ? ? <div class="voice-container">
? ? ? ? <button class="voice-button" @click="voiceButtonClick">
? ? ? ? ? <icon-volume-notice v-if="!voiceMute" theme="outline" size="23" fill="#939393" :strokeWidth="3"
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? strokeLinejoin="miter" strokeLinecap="butt"/>
? ? ? ? ? <icon-volume-mute v-if="voiceMute" theme="outline" size="23" fill="#939393" :strokeWidth="3"
? ? ? ? ? ? ? ? ? ? ? ? ? ? strokeLinejoin="miter" strokeLinecap="butt"/>
? ? ? ? </button>
? ? ? ? <div class="voice-slider">
? ? ? ? ? <el-slider
? ? ? ? ? ? ? v-model="voicePower"
? ? ? ? ? ? ? :max="1"
? ? ? ? ? ? ? :step="0.1"
? ? ? ? ? ? ? size="small"
? ? ? ? ? ? ? @change="changeVoicePower"/>
? ? ? ? </div>
? ? ? </div>
? ? </div>

? ? <audio
? ? ? ? ref="musicAudio"
? ? ? ? class="audio-component"
? ? ? ? controls
? ? ? ? preload="auto"
? ? ? ? @canplay="changeDuration">
? ? ? <source ref="musicSource" type="audio/mpeg"/>
? ? </audio>
? </div>

</template>

<script>
import {computed, onMounted, onUnmounted, reactive, ref, watch} from "vue";

//这里是自己封装的axios请求,可以将这里替换成自己的请求逻辑
import requests from "@/api/ajax";

export default {
? name: "index",
? setup() {

? ? //是否正在播放
? ? const playState = ref(false);

? ? //现在的播放时间
? ? const playTime = ref(0.00);

? ? //歌曲的时间长度
? ? const playDuration = ref(0.00);

? ? //进度条长度
? ? const sliderLength = ref(100);

? ? //歌曲URL
? ? const musicUrl = ref("");

? ? //播放器标签
? ? const musicAudio = ref(null);

? ? //实现音乐播放的标签
? ? const musicSource = ref(null);

? ? //是否静音
? ? const voiceMute = ref(false);

? ? //音量大小
? ? const voicePower = ref(0.5);

? ? const musicState = reactive({
? ? ? musicArr: [],
? ? ? musicCount: 0
? ? })

? ? const musicCursor = ref(0);

? ? //页面偏移量
? ? const pageOffset = ref(0)

? ? //是否达到阈值,达到阈值就显示播放器,反之
? ? const offsetThreshold = ref(false)

? ? //激活播放器
? ? const operateMusicPlayer = () => {
? ? ? pageOffset.value = window.scrollY
? ? ? //当页面滚动偏移达到800,激活用户框
? ? ? if (pageOffset.value > 800) {
? ? ? ? offsetThreshold.value = true
? ? ? } else {
? ? ? ? //反之
? ? ? ? offsetThreshold.value = false
? ? ? }
? ? }

? ? //播放按钮点击回调
? ? const playButtonClick = () => {

? ? ? if (playState.value) {
? ? ? ? musicAudio.value.pause()
? ? ? } else {
? ? ? ? musicAudio.value.play()
? ? ? }

? ? ? //修改播放时间【设置这个,当一首歌正常播放结束之后,再次点击播放按钮,进度条会得到重置】
? ? ? playTime.value = musicAudio.value.currentTime

? ? ? //重新设置播放状态
? ? ? playState.value = !playState.value
? ? }

? ? //上一曲按钮点击回调
? ? const lastButtonClick = () => {
? ? ? musicCursor.value -= 1

? ? ? changeMusic()
? ? }

? ? //下一曲按钮点击回调
? ? const nextButtonClick = () => {
? ? ? musicCursor.value += 1

? ? ? changeMusic()
? ? }

? ? //歌曲进度条文本提示
? ? const tooltipFormat = (val) => {

? ? ? let strTime = playTime.value

? ? ? let strMinute = parseInt(strTime / 60 + '')

? ? ? let strSecond = parseInt(strTime % 60 + '')

? ? ? return strMinute + ":" + strSecond
? ? }

? ? //当歌曲能播放时【亦即在canplay钩子函数中】,musicAudio.value.duration才不会是NaN,才能进行歌曲长度的设置
? ? const changeDuration = () => {
? ? ? if (playDuration.value != musicAudio.value.duration) {

? ? ? ? //修改进度条的最大值
? ? ? ? sliderLength.value = musicAudio.value.duration

? ? ? ? //修改歌曲播放时间
? ? ? ? playDuration.value = musicAudio.value.duration
? ? ? }
? ? }

? ? //el-slider的钩子函数,拖动进度条时快进歌曲,改变当前播放进度
? ? const changePlayTime = (val) => {
? ? ? musicAudio.value.currentTime = val
? ? }

? ? //音量按钮点击回调
? ? const voiceButtonClick = () => {
? ? ? voiceMute.value = !voiceMute.value

? ? ? if (!voiceMute.value) {
? ? ? ? voicePower.value = 1

? ? ? ? musicAudio.value.volume = 1
? ? ? } else {
? ? ? ? voicePower.value = 0

? ? ? ? musicAudio.value.volume = 0
? ? ? }
? ? }

? ? //el-slider的钩子函数,用于调节音量
? ? const changeVoicePower = (val) => {
? ? ? musicAudio.value.volume = val

? ? ? voicePower.value = val

? ? ? if (val > 0) {
? ? ? ? voiceMute.value = false
? ? ? } else {
? ? ? ? voiceMute.value = true
? ? ? }

? ? }

? ? //播放状态下,进度条里的数值每秒递增。而Audio因为在播放状态下,currentTime会自己递增,所以不用处理
? ? const updatePlayTimePerSecond = () => {
? ? ? if (playState.value) {
? ? ? ? playTime.value += 1

? ? ? ? if (playTime.value >= playDuration.value) {
? ? ? ? ? //代表当前歌曲已经播放完毕,进行切歌
? ? ? ? ? musicCursor.value++

? ? ? ? ? changeMusic()
? ? ? ? }
? ? ? }
? ? }

? ? //切歌
? ? const changeMusic = () => {
? ? ? //切歌【这里的music_url是后端返回给前端的json字符串中,用于存储歌曲在线链接的属性名是:music_url,所以要实现自己的请求逻辑,将这里的music_url改为自己的即可】
? ? ? musicSource.value.src = musicState.musicArr[musicCursor.value % musicState.musicCount].music_url

? ? ? // 当刷新了url之后,需要执行load方法才能播放这个音乐
? ? ? musicAudio.value.load()

? ? ? playTime.value = musicAudio.value.currentTime

? ? ? sliderLength.value = musicAudio.value.duration

? ? ? musicAudio.value.play()

? ? ? playState.value = true
? ? }

? ? //初始化歌曲源【将这里替换成自己的请求逻辑】
? ? const initMusicArr = () => {
? ? ? requests.get("/Music/QueryAllMusic").then(function (res) {
? ? ? ? musicState.musicArr = res

? ? ? ? musicState.musicCount = res.length

? ? ? })
? ? }

? ? onMounted(() => {
? ? ? initMusicArr()

? ? ? //播放状态下,使播放进度自增1,以与Audio内置的currentTime相匹配
? ? ? setInterval(updatePlayTimePerSecond, 1000)

? ? ? //添加滚动事件
? ? ? window.addEventListener("scroll", operateMusicPlayer)
? ? })

? ? onUnmounted(() => {
? ? ? window.removeEventListener("scroll", operateMusicPlayer)
? ? })


? ? return {
? ? ? musicAudio,
? ? ? musicSource,
? ? ? playState,
? ? ? playTime,
? ? ? playDuration,
? ? ? sliderLength,
? ? ? musicUrl,
? ? ? voiceMute,
? ? ? voicePower,
? ? ? musicState,
? ? ? musicCursor,
? ? ? pageOffset,
? ? ? offsetThreshold,
? ? ? playButtonClick,
? ? ? lastButtonClick,
? ? ? nextButtonClick,
? ? ? voiceButtonClick,
? ? ? tooltipFormat,
? ? ? changeMusic,
? ? ? changeDuration,
? ? ? changePlayTime,
? ? ? changeVoicePower,
? ? ? updatePlayTimePerSecond,
? ? ? initMusicArr
? ? }
? },
}
</script>

<style scoped>

.music-container {
? position: fixed;
? justify-content: center;
? width: 280px;
? height: 110px;
? background-color: white;
? border-radius: 15px;
? bottom: 15px;
? left: 10px;
? opacity: 0;
? transition: 0.5s;
}


.music-disk {
? position: absolute;
? width: 90px;
? height: 90px;
? left: 15px;
? top: 10px;
? border-radius: 50%;
}

.music-disk-picture {
? width: 90px;
? height: 90px;
? border-radius: 50%;
? /*设置图片不可点击*/
? pointer-events: none;
}

.music-disk-playing-style {
? animation: music-disk-rotate 5s linear infinite;
}

@keyframes music-disk-rotate {
? 0% {
? ? transform: rotate(0deg);
? }
? 100% {
? ? transform: rotate(360deg);
? }
}

.button-group {
? position: absolute;
? width: 330px;
? height: 38px;
? left: 90px;
? bottom: 13px;
? margin-left: 10px;
}

.button-group > button {
? margin-left: 10px;
}

.play-button {
? float: left;
? width: 31px;
? height: 31px;
? padding: 4px;
? /*margin: 0px;*/
? border: 0px;
? border-radius: 50%;
? margin: 7px 0px 0px 0px;
}

.voice-button {
? float: left;
? width: 31px;
? height: 31px;
? padding: 0px;
? /*margin: 0px;*/
? border: 0px;
? border-radius: 50%;
? margin: 7px 0px 0px 0px;
? background-color: transparent;
}


.music-slider {
? position: absolute;
? top: 20px;
? left: 120px;
? width: 50%;
}

.voice-container {
? float: left;
? margin-left: 12px;
? width: 31px;
? height: 38px;
? overflow: hidden !important;
? transition: 0.5s;
}

.voice-container:hover {
? width: 160px;
}


.voice-slider {
? position: relative;
? top: 2px;
? right: -30px;
? width: 90px;
? height: 35px;
? background-color: white;
? border-radius: 10px;
? padding: 0px 15px 0px 15px;
? transition: 0.2s;
}

.audio-component {
? width: 300px;
? height: 200px;
? top: 100px;
? display: none;
}

.music-active-switch{
? opacity: 1;
}

</style>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

查看更多关于Vue3实现简易音乐播放器组件的详细内容...

  阅读:43次