8

Vue开发历程---音乐播放器的继续

 1 year ago
source link: https://blog.51cto.com/u_15807146/5808289
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前面一篇文章​ ​Vue开发历程---音乐播放器​​,实现了播放音乐,以及基本的布局。但是进度条没有实现同步,本文即介绍进度条的实现。

一、效果图

Vue开发历程---音乐播放器的继续_Vue

二、心路历程

1、elementUI 滑块的使用

<div class="block" style="margin-top:5px">
<el-slider v-model="value" @change="updateSlideTime()" :format-tooltip="formatTooltip"></el-slider>
</div>

2、JS的编写

进度条的状态是实时的,也就是每一秒每一刻都要变化,如何实现这一功能? 首先需要写一个方法,每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里,所以也是用到了组件的通信。

3、时间的转化

由于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友,一定一定要注重基础的深入学习。勉之哉!!


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK