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

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

@TOC


前言

前面一篇文章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友,一定一定要注重基础的深入学习。勉之哉!!

相关文章
|
13天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
28 1
vue学习第四章
|
13天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
26 1
vue学习第九章(v-model)
|
13天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
29 1
vue学习第十章(组件开发)
|
18天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
26 3
|
18天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
37 2
|
18天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
19天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
19天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
19天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
20天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。