Vue开发历程---音乐播放器的继续
文章目录
- 前面一篇文章Vue开发历程---音乐播放器,实现了播放音乐,以及基本的布局。但是进度条没有实现同步,本文即介绍进度条的实现。
-
- <div class="block" style="margin-top:5px"> <el-slider v-model="value" @change="updateSlideTime()" :format-tooltip="formatTooltip"></el-slider> </div>
- 进度条的状态是实时的,也就是每一秒每一刻都要变化,如何实现这一功能? 首先需要写一个方法,每30ms更新一次状态,同时在组件挂载完成的时候调用这个方法,从而实现了每30ms,即更新进度条的数值。 通过refs可以获得audio这个标签,这个标签上有duration(时长),currentTime(当前时间)这两个属性,通过这两个属性,可以计算出来进度的占比。 updateSongsTime: function () { const timer = setInterval(() => { const music = document.getElementById("m_mp3")||null; if (music != null) { this.currentTime = music.currentTime; this.duration = music.duration; this.persent = (this.currentTime / this.duration) * 100; } }, 30); mounted() { this.songNum = this.songNames.length; const music = this.$refs.player; this.updateSongsTime(); } 同时也需要在MusicPlayer.vue组件里面如法炮制,获取父组件的值从而实时更新滑块的状态,因为滑块是写在MusicPlayer组件里面了,但是歌曲的播放在父组件Player.vue里,所以也是用到了组件的通信。
- 由于currentTime返回来的是总秒数,而音乐播放器一般都是分:秒这样的格式,所以通过一些简单的数学计算(除以60求分,对60取余求秒)来进行对时长的格式化处理。采用了三目运算符,还对不足10的时间进行了处理,即小于10的数值前面自动补0 formatTooltip() { return Math.trunc(this.$parent.currentTime/60)+":"+((Math.trunc(this.$parent.currentTime%60)<10)?("0"+Math.trunc(this.$parent.currentTime%60)):Math.trunc(this.$parent.currentTime%60)); } #源代码 代码如下: Player.vue <template> <div class="player_div1"> <transition name="open" mode="out-in"> <component v-bind:is="view" @hello="changeSlideState" @play="changePlayState" @lastSongs="lastSongs" @nextSongs="nextSongs" @changeSongsTime="changeSongsTime"></component> </transition> <audio class="m_mp3" id="m_mp3" :src="this.songNames[this.index].Url" autoplay loop ref="player" controls style="display:none"> </audio> </div></template><script>import HidePlayer from '@/part/HidePlayer'import MusicPlayer from '@/part/MusicPlayer'export default { name: 'Player', data() { return { view: MusicPlayer, isClose: false, isShow: true, isRun: 'come', index: 0, songNum: 2, currentTime: 0, duration: 0, persent: 0, songNames: [ { id: 6, name: '庐州月-许嵩', Url: require('@/assets/庐州月.mp3'), png: require('@/assets/庐州月.png'), }, { id: 7, name: '燕归巢-许嵩', Url: require('@/assets/燕归巢.mp3'), png: require('@/assets/燕归巢.jpg'), }, { id: 1, name: '张韶涵-篇章', Url: require('@/assets/张韶涵-篇章.mp3'), png: require('@/assets/篇章.png'), }, { id: 2, name: '爱就一个字 抒情版', Url: require('@/assets/爱就一个字 抒情版.mp3'), png: require('@/assets/爱就一个字.png'), }, { id: 3, name: '最伟大的作品-周杰伦', Url: require('@/assets/最伟大的作品-周杰伦.mp3'), png: require('@/assets/周杰伦.jpg'), }, { id: 4, name: '等你下课 (with 杨瑞代)-周杰伦', Url: require('@/assets/等你下课 (with 杨瑞代)-周杰伦.mp3'), png: require('@/assets/等你下课.png'), }, { id: 5, name: '告白气球-周杰伦', Url: require('@/assets/告白气球-周杰伦.mp3'), png: require('@/assets/告白气球.png'), }, { id: 6, name: '还在流浪-周杰伦', Url: require('@/assets/还在流浪-周杰伦.mp3'), png: require('@/assets/还在流浪.png'), }, ] } }, components: { HidePlayer, MusicPlayer }, methods: { changeSlideState() { this.isClose = !this.isClose; if (this.isClose) { this.view = HidePlayer; } else { this.view = MusicPlayer; } }, changePlayState() { if (!this.isShow) { this.isShow = true; this.isRun = "come"; document.getElementById("m_mp3").pause(); } else { this.isShow = false; this.isRun = "go"; var my_mp3 = document.getElementById("m_mp3"); my_mp3.play(); } }, nextSongs() { if (this.isShow) { this.isShow = false; this.isRun = "go"; } this.index = (this.index + 1) % this.songNum; }, lastSongs() { if (this.isShow) { this.isShow = false; this.isRun = "go"; } if (this.index == 0) { this.index = this.songNum - 1; } else { this.index = this.index - 1; } }, changeSongsTime(value) { const music = document.getElementById("m_mp3"); music.currentTime = value / 100 * music.duration; } , updateSongsTime: function () { const timer = setInterval(() => { const music = document.getElementById("m_mp3")||null; if (music != null) { this.currentTime = music.currentTime; this.duration = music.duration; this.persent = (this.currentTime / this.duration) * 100; } }, 30) } }, mounted() { this.songNum = this.songNames.length; const music = this.$refs.player; this.updateSongsTime(); }}</script><style scoped>.open-enter-active { animation: slide-in linear 0.5s;}.open-leave-active { animation: slide-in reverse linear 0.5s;}@keyframes slide-in { from { transform: translateX(-100%); } to { transform: translateX(0%); }}</style> MusicPlayer.vue <template> <div class="music"> <!-- 占位 --> <div class="m_hold"> </div> <div class="m_img"> <img :src="this.$parent.songNames[this.$parent.index].png" width="90px" :class="this.$parent.isRun"> </div> <!-- 歌曲信息 --> <div class="m_text"> {{ this.$parent.songNames[this.$parent.index].name }} <div class="block" style="margin-top:5px"> <el-slider v-model="value" @change="updateSlideTime()" :format-tooltip="formatTooltip"></el-slider> </div> </div> <!-- 音乐时间信息 --> <div class="m_time"> <span>{{nowtime}} / </span> <span>{{allTime}}</span> </div> <!-- 按钮 --> <div class="m_btn"> <a href="#" class="m_prev" @click="playLastSong()"></a> <a href="#" class="m_play" @click="changeState()" v-show="this.$parent.isShow"></a> <a href="#" class="m_pause" @click="changeState()" v-show="!this.$parent.isShow"></a> <a href="#" class="m_next" @click="playNextSong()"></a> </div> <!-- 折叠功能 --> <div class="m_close" @click="changeCloseState()"> <a href=""></a> </div> </div></template><script>export default { name: 'MusicPlayer', data() { return { songName: '', value:0.0, } }, methods: { changeState() { this.$emit("play") }, changeCloseState() { this.$emit("hello"); }, playNextSong() { this.$emit("nextSongs"); this.songName = this.$parent.songNames[this.$parent.index].name }, playLastSong() { this.$emit("lastSongs"); this.songName = this.$parent.songNames[this.$parent.index].name }, changeSlideTime() { const timer = setInterval(()=>{ this.value = this.$parent.persent; },30) }, updateSlideTime() { this.$emit("changeSongsTime",this.value); }, formatTooltip() { return Math.trunc(this.$parent.currentTime/60)+":"+((Math.trunc(this.$parent.currentTime%60)<10)?("0"+Math.trunc(this.$parent.currentTime%60)):Math.trunc(this.$parent.currentTime%60)); } }, watch: { }, computed:{ nowtime() { return ((Math.trunc(this.$parent.currentTime/60)<10)?("0"+Math.trunc(this.$parent.currentTime/60)):(Math.trunc(this.$parent.currentTime/60)))+":"+((Math.trunc(this.$parent.currentTime%60)<10)?("0"+Math.trunc(this.$parent.currentTime%60)):(Math.trunc(this.$parent.currentTime%60))); }, allTime() { return ((Math.trunc(this.$parent.duration/60)<10)?("0"+Math.trunc(this.$parent.duration/60)):(Math.trunc(this.$parent.duration/60)))+":"+((Math.trunc(this.$parent.duration%60)<10)?("0"+Math.trunc(this.$parent.duration%60)):(Math.trunc(this.$parent.duration%60))); } } ,mounted() { this.songName = this.$parent.songNames[this.$parent.index].name; this.changeSlideTime(); }}</script><style scoped>/* 关于播放器的样式 */.music { width: 100%; height: 120px; background: black; /* 相对浏览器定位 */ position: fixed; left: 0px; bottom: 0px; /* bottom: 100px; */ /* border-bottom: 50px; */ /* 透明度 */ opacity: 0.8; /* 阴影值 */ box-shadow: 10px 15px 15px 1px black}.music .m_hold { float: left; width: 90px; height: 90px;}/* 调整音乐盒图片 */.music .m_img { margin-top: 15px; margin-left: 10px; margin-right: 10px; /* 左浮动 */ float: left; width: 90px; height: 90px; border-radius: 50%; overflow: hidden;}/* 修改文字 */.music .m_text { /* 左浮动 */ float: left; color: white; font-size: 20px; /* 字体加粗 */ font-weight: bold; margin-top: 25px; margin-left: 20px; margin-bottom: 10px; width: 25%;}/* 使得所有a标签一起移动 */.music .m_btn { float: left; position: absolute; /* 绝对定位:防止歌曲名称过长,挤出div */ left: 48%;}/* 修改a标签 */.music .m_btn a { width: 32px; height: 32px; float: left; margin-top: 50px; margin-left: 20px; background: url(@/assets/player_bg.png);}.music .m_btn .m_prev { background-position: -69px 0px;}.music .m_btn .m_next { background-position: -150px 0px;}.music .m_btn .m_play { background-position: -107px -5px;}.music .m_btn .m_prev:hover { background-position: -69px -32px;}.music .m_btn .m_next:hover { background-position: -150px -32px;}.music .m_btn .m_play:hover { background-position: -107px -47px;}.music .m_btn .m_pause { background-position: -292px -94px;}.music .m_btn .m_pause:hover { background-position: -334px -94px;}/* 还有一个悬停 没写 *//* 设置最右边的关闭样式 */.music .m_close { float: right; background: white; cursor: pointer; width: 23px; height: 100px; margin-top: 10px; background: url(@/assets/player_bg.png);}.m_time{ width: 100px; height: 20px; margin-top: 64px; float:left; margin-left: 10px; font:bold}/* 设置最右边的关闭样式 */.music_hide { float: left; background: white; cursor: pointer; width: 23px; height: 100px; margin-top: 2px;}.go { animation: bounce-in 2s linear infinite;}.come { animation: none;}@keyframes bounce-in { from { transform: rotate(0deg); } to { transform: rotate(360deg); }}.open-enter-active { animation: slide-in linear 0.5s;}.open-leave-active { animation: slide-in reverse linear 0.5s;}@keyframes slide-in { from { transform: translateX(-100%); } to { transform: translateX(0%); }}</style> HidePlayer.vue <template> <div class="music_hide" @click="changeCloseState()"><a href="#" class="m_open"></a></div></template><script>export default { name:'HidePlayer', methods:{ changeCloseState() { this.$emit("hello"); } }}</script><style scoped>.music_hide { float: left; background: url(@/assets/player_bg.png); cursor: pointer; width: 23px; height: 100px; margin-top: 10px; bottom: 0px; /* bottom: 100px; */ position: fixed; background-position-x: -45px;}</style>
- 真可谓是基础不牢,地动山摇,由于CSS,JS学的很浅,现在很多的布局搞不明白,布局效果只有一点点的尝试,导致自己的开发进度变得特别特别缓慢,再次提醒各位C友,一定一定要注重基础的深入学习。勉之哉!!
前面一篇文章Vue开发历程---音乐播放器,实现了播放音乐,以及基本的布局。但是进度条没有实现同步,本文即介绍进度条的实现。

<div class="block" style="margin-top:5px">
<el-slider v-model="value" @change="updateSlideTime()" :format-tooltip="formatTooltip"></el-slider>
</div>
<el-slider v-model="value" @change="updateSlideTime()" :format-tooltip="formatTooltip"></el-slider>
</div>
进度条的状态是实时的,也就是每一秒每一刻都要变化,如何实现这一功能? 首先需要写一个方法,每30ms更新一次状态,同时在组件挂载完成的时候调用这个方法,从而实现了每30ms,即更新进度条的数值。 通过refs可以获得audio这个标签,这个标签上有duration(时长),currentTime(当前时间)这两个属性,通过这两个属性,可以计算出来进度的占比。
updateSongsTime: function () {
const timer = setInterval(() => {
const music = document.getElementById("m_mp3")||null;
if (music != null) {
this.currentTime = music.currentTime;
this.duration = music.duration;
this.persent = (this.currentTime / this.duration) * 100;
}
}, 30);
mounted() {
this.songNum = this.songNames.length;
const music = this.$refs.player;
this.updateSongsTime();
}
同时也需要在MusicPlayer.vue组件里面如法炮制,获取父组件的值从而实时更新滑块的状态,因为滑块是写在MusicPlayer组件里面了,但是歌曲的播放在父组件Player.vue里,所以也是用到了组件的通信。
由于currentTime返回来的是总秒数,而音乐播放器一般都是分:秒这样的格式,所以通过一些简单的数学计算(除以60求分,对60取余求秒)来进行对时长的格式化处理。采用了三目运算符,还对不足10的时间进行了处理,即小于10的数值前面自动补0
formatTooltip()
{
return Math.trunc(this.$parent.currentTime/60)+":"+((Math.trunc(this.$parent.currentTime%60)<10)?("0"+Math.trunc(this.$parent.currentTime%60)):Math.trunc(this.$parent.currentTime%60));
}
#源代码
代码如下: Player.vue
<template>
<div class="player_div1">
<transition name="open" mode="out-in">
<component v-bind:is="view" @hello="changeSlideState" @play="changePlayState" @lastSongs="lastSongs"
@nextSongs="nextSongs" @changeSongsTime="changeSongsTime"></component>
</transition>
<audio class="m_mp3" id="m_mp3" :src="this.songNames[this.index].Url" autoplay loop ref="player" controls
style="display:none">
</audio>
</div>
</template>
<script>
import HidePlayer from '@/part/HidePlayer'
import MusicPlayer from '@/part/MusicPlayer'
export default {
name: 'Player',
data() {
return {
view: MusicPlayer,
isClose: false,
isShow: true,
isRun: 'come',
index: 0,
songNum: 2,
currentTime: 0,
duration: 0,
persent: 0,
songNames: [
{
id: 6,
name: '庐州月-许嵩',
Url: require('@/assets/庐州月.mp3'),
png: require('@/assets/庐州月.png'),
},
{
id: 7,
name: '燕归巢-许嵩',
Url: require('@/assets/燕归巢.mp3'),
png: require('@/assets/燕归巢.jpg'),
},
{
id: 1,
name: '张韶涵-篇章',
Url: require('@/assets/张韶涵-篇章.mp3'),
png: require('@/assets/篇章.png'),
},
{
id: 2,
name: '爱就一个字 抒情版',
Url: require('@/assets/爱就一个字 抒情版.mp3'),
png: require('@/assets/爱就一个字.png'),
},
{
id: 3,
name: '最伟大的作品-周杰伦',
Url: require('@/assets/最伟大的作品-周杰伦.mp3'),
png: require('@/assets/周杰伦.jpg'),
},
{
id: 4,
name: '等你下课 (with 杨瑞代)-周杰伦',
Url: require('@/assets/等你下课 (with 杨瑞代)-周杰伦.mp3'),
png: require('@/assets/等你下课.png'),
},
{
id: 5,
name: '告白气球-周杰伦',
Url: require('@/assets/告白气球-周杰伦.mp3'),
png: require('@/assets/告白气球.png'),
},
{
id: 6,
name: '还在流浪-周杰伦',
Url: require('@/assets/还在流浪-周杰伦.mp3'),
png: require('@/assets/还在流浪.png'),
},
]
}
},
components: {
HidePlayer,
MusicPlayer
},
methods: {
changeSlideState() {
this.isClose = !this.isClose;
if (this.isClose) {
this.view = HidePlayer;
} else {
this.view = MusicPlayer;
}
},
changePlayState() {
if (!this.isShow) {
this.isShow = true;
this.isRun = "come";
document.getElementById("m_mp3").pause();
} else {
this.isShow = false;
this.isRun = "go";
var my_mp3 = document.getElementById("m_mp3");
my_mp3.play();
}
},
nextSongs() {
if (this.isShow) {
this.isShow = false;
this.isRun = "go";
}
this.index = (this.index + 1) % this.songNum;
},
lastSongs() {
if (this.isShow) {
this.isShow = false;
this.isRun = "go";
}
if (this.index == 0) {
this.index = this.songNum - 1;
} else {
this.index = this.index - 1;
}
},
changeSongsTime(value) {
const music = document.getElementById("m_mp3");
music.currentTime = value / 100 * music.duration;
}
,
updateSongsTime: function () {
const timer = setInterval(() => {
const music = document.getElementById("m_mp3")||null;
if (music != null) {
this.currentTime = music.currentTime;
this.duration = music.duration;
this.persent = (this.currentTime / this.duration) * 100;
}
}, 30)
}
}, mounted() {
this.songNum = this.songNames.length;
const music = this.$refs.player;
this.updateSongsTime();
}
}
</script>
<style scoped>
.open-enter-active {
animation: slide-in linear 0.5s;
}
.open-leave-active {
animation: slide-in reverse linear 0.5s;
}
@keyframes slide-in {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0%);
}
}
</style>
MusicPlayer.vue
<template>
<div class="music">
<!-- 占位 -->
<div class="m_hold">
</div>
<div class="m_img">
<img :src="this.$parent.songNames[this.$parent.index].png" width="90px" :class="this.$parent.isRun">
</div>
<!-- 歌曲信息 -->
<div class="m_text">
{{ this.$parent.songNames[this.$parent.index].name }}
<div class="block" style="margin-top:5px">
<el-slider v-model="value" @change="updateSlideTime()" :format-tooltip="formatTooltip"></el-slider>
</div>
</div>
<!-- 音乐时间信息 -->
<div class="m_time">
<span>{{nowtime}} / </span>
<span>{{allTime}}</span>
</div>
<!-- 按钮 -->
<div class="m_btn">
<a href="#" class="m_prev" @click="playLastSong()"></a>
<a href="#" class="m_play" @click="changeState()" v-show="this.$parent.isShow"></a>
<a href="#" class="m_pause" @click="changeState()" v-show="!this.$parent.isShow"></a>
<a href="#" class="m_next" @click="playNextSong()"></a>
</div>
<!-- 折叠功能 -->
<div class="m_close" @click="changeCloseState()">
<a href=""></a>
</div>
</div>
</template>
<script>
export default {
name: 'MusicPlayer',
data() {
return {
songName: '',
value:0.0,
}
},
methods: {
changeState() {
this.$emit("play")
},
changeCloseState() {
this.$emit("hello");
},
playNextSong() {
this.$emit("nextSongs");
this.songName = this.$parent.songNames[this.$parent.index].name
},
playLastSong() {
this.$emit("lastSongs");
this.songName = this.$parent.songNames[this.$parent.index].name
},
changeSlideTime()
{
const timer = setInterval(()=>{
this.value = this.$parent.persent;
},30)
},
updateSlideTime()
{
this.$emit("changeSongsTime",this.value);
},
formatTooltip()
{
return Math.trunc(this.$parent.currentTime/60)+":"+((Math.trunc(this.$parent.currentTime%60)<10)?("0"+Math.trunc(this.$parent.currentTime%60)):Math.trunc(this.$parent.currentTime%60));
}
},
watch:
{
},
computed:{
nowtime()
{
return ((Math.trunc(this.$parent.currentTime/60)<10)?("0"+Math.trunc(this.$parent.currentTime/60)):(Math.trunc(this.$parent.currentTime/60)))+":"+((Math.trunc(this.$parent.currentTime%60)<10)?("0"+Math.trunc(this.$parent.currentTime%60)):(Math.trunc(this.$parent.currentTime%60)));
},
allTime()
{
return ((Math.trunc(this.$parent.duration/60)<10)?("0"+Math.trunc(this.$parent.duration/60)):(Math.trunc(this.$parent.duration/60)))+":"+((Math.trunc(this.$parent.duration%60)<10)?("0"+Math.trunc(this.$parent.duration%60)):(Math.trunc(this.$parent.duration%60)));
}
}
,mounted() {
this.songName = this.$parent.songNames[this.$parent.index].name;
this.changeSlideTime();
}
}
</script>
<style scoped>
/* 关于播放器的样式 */
.music {
width: 100%;
height: 120px;
background: black;
/* 相对浏览器定位 */
position: fixed;
left: 0px;
bottom: 0px;
/* bottom: 100px; */
/* border-bottom: 50px; */
/* 透明度 */
opacity: 0.8;
/* 阴影值 */
box-shadow: 10px 15px 15px 1px black
}
.music .m_hold {
float: left;
width: 90px;
height: 90px;
}
/* 调整音乐盒图片 */
.music .m_img {
margin-top: 15px;
margin-left: 10px;
margin-right: 10px;
/* 左浮动 */
float: left;
width: 90px;
height: 90px;
border-radius: 50%;
overflow: hidden;
}
/* 修改文字 */
.music .m_text {
/* 左浮动 */
float: left;
color: white;
font-size: 20px;
/* 字体加粗 */
font-weight: bold;
margin-top: 25px;
margin-left: 20px;
margin-bottom: 10px;
width: 25%;
}
/* 使得所有a标签一起移动 */
.music .m_btn {
float: left;
position: absolute;
/* 绝对定位:防止歌曲名称过长,挤出div */
left: 48%;
}
/* 修改a标签 */
.music .m_btn a {
width: 32px;
height: 32px;
float: left;
margin-top: 50px;
margin-left: 20px;
background: url(@/assets/player_bg.png);
}
.music .m_btn .m_prev {
background-position: -69px 0px;
}
.music .m_btn .m_next {
background-position: -150px 0px;
}
.music .m_btn .m_play {
background-position: -107px -5px;
}
.music .m_btn .m_prev:hover {
background-position: -69px -32px;
}
.music .m_btn .m_next:hover {
background-position: -150px -32px;
}
.music .m_btn .m_play:hover {
background-position: -107px -47px;
}
.music .m_btn .m_pause {
background-position: -292px -94px;
}
.music .m_btn .m_pause:hover {
background-position: -334px -94px;
}
/* 还有一个悬停 没写 */
/* 设置最右边的关闭样式 */
.music .m_close {
float: right;
background: white;
cursor: pointer;
width: 23px;
height: 100px;
margin-top: 10px;
background: url(@/assets/player_bg.png);
}
.m_time
{
width: 100px;
height: 20px;
margin-top: 64px;
float:left;
margin-left: 10px;
font:bold
}
/* 设置最右边的关闭样式 */
.music_hide {
float: left;
background: white;
cursor: pointer;
width: 23px;
height: 100px;
margin-top: 2px;
}
.go {
animation: bounce-in 2s linear infinite;
}
.come {
animation: none;
}
@keyframes bounce-in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.open-enter-active {
animation: slide-in linear 0.5s;
}
.open-leave-active {
animation: slide-in reverse linear 0.5s;
}
@keyframes slide-in {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0%);
}
}
</style>
HidePlayer.vue
<template>
<div class="music_hide" @click="changeCloseState()"><a href="#" class="m_open"></a></div>
</template>
<script>
export default {
name:'HidePlayer',
methods:{
changeCloseState()
{
this.$emit("hello");
}
}
}
</script>
<style scoped>
.music_hide {
float: left;
background: url(@/assets/player_bg.png);
cursor: pointer;
width: 23px;
height: 100px;
margin-top: 10px;
bottom: 0px;
/* bottom: 100px; */
position: fixed;
background-position-x: -45px;
}
</style>
真可谓是基础不牢,地动山摇,由于CSS,JS学的很浅,现在很多的布局搞不明白,布局效果只有一点点的尝试,导致自己的开发进度变得特别特别缓慢,再次提醒各位C友,一定一定要注重基础的深入学习。勉之哉!!
© 版权声明
文章版权归作者所有,未经允许请勿转载。