前言
用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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did122202