Vue开发历程---音乐播放器的继续
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.
前面一篇文章 Vue开发历程---音乐播放器,实现了播放音乐,以及基本的布局。但是进度条没有实现同步,本文即介绍进度条的实现。
一、效果图
二、心路历程
1、elementUI 滑块的使用
<el-slider v-model="value" @change="updateSlideTime()" :format-tooltip="formatTooltip"></el-slider>
</div>
2、JS的编写
进度条的状态是实时的,也就是每一秒每一刻都要变化,如何实现这一功能? 首先需要写一个方法,每30ms更新一次状态,同时在组件挂载完成的时候调用这个方法,从而实现了每30ms,即更新进度条的数值。 通过refs可以获得audio这个标签,这个标签上有duration(时长),currentTime(当前时间)这两个属性,通过这两个属性,可以计算出来进度的占比。
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);
this.songNum = this.songNames.length;
const music = this.$refs.player;
this.updateSongsTime();
}
同时也需要在MusicPlayer.vue组件里面如法炮制,获取父组件的值从而实时更新滑块的状态,因为滑块是写在MusicPlayer组件里面了,但是歌曲的播放在父组件Player.vue里,所以也是用到了组件的通信。
3、时间的转化
由于currentTime返回来的是总秒数,而音乐播放器一般都是分:秒这样的格式,所以通过一些简单的数学计算(除以60求分,对60取余求秒)来进行对时长的格式化处理。采用了三目运算符,还对不足10的时间进行了处理,即小于10的数值前面自动补0
{
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
<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
<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
<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
-
132
原生JS实现音乐播放器! 前 言 最近在复习JS,觉得音乐播放器是个挺有意思...
-
37
土狗音乐播放器 一个基于 electron-vue 开发的音乐播放器 (持续更新中..欢迎star) 运行 git clone https://github.com/SmallRuralDog/electron-vue-music.git cd electron-vue-music npm install # s...
-
31
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由QQ音乐技术团队发表于云+社区专栏 一、问题背景与分析 不久前,团队发现其Android平台App在播放MV视频《凤凰花开的路口》时,会带有如电流声一般的杂音,这影响了用户体验。 研发同学在
-
61
我们还是会怀念用千千静听循环《晴天》的那个夏天
-
11
重新做一个简单的小项目,在一年后,争取进入android技术开发的行列中。 至于一直未能加入技术开发行业的原因,本博客将会进行展开,其实也是总所周知的原因。 本篇文章其实挺突然的,从笔记就突然变成了一篇分享出来的博客,记录自己的“重学成长...
-
8
Vue+Electron从零开始打造一个本地播放器爱前端不爱恋爱微信公众号:web前端学习圈,关注领取85G前端全套系统教程
-
3
.net工程师学习vue的心路历程(一) 实习一年后,想做一个属于自己的博客网站,准备用core api去搭建服务端接口,前端准备采用vue这样的一...
-
6
坎爷继续推广自己的播放器 声称自己推掉了苹果1亿美元的合约 2022年02月21日07:36 新浪数码 我有话说(42人参与) 收...
-
7
开发一个web音乐播放器到底有多难? 作者: 一个球 发布时间: 2019-08-22 上次修改:2021-12-14 前段时间比较闲,顺...
-
7
本人基于SwiftUI开发的macos本地音乐播放器 fmusic 开源了 wandercn...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK