资讯专栏INFORMATION COLUMN

用Vue搭建一个应用盒子(三):音乐播放器

appetizerio / 2499人阅读

摘要:组件结构接着我们就该搭建这个播放器的组件了。总的原理是首先获取音频的持续时间,然后通过一个定时器,不断更新显示时间,播放完成时,计时器停止。这个页面比较简单,播放器标签,绑定了事件,即播放完成后执行。

这个播放器的开发历时2个多月,并不是说它有多复杂,相反它的功能还非常不完善,仅具雏形。之所以磨磨蹭蹭这么久,一是因为拖延,二也是实习公司项目太紧。8月底结束实习前写完了样式,之后在家空闲时间多了,集中精力就把JS部分做完了。

这个播放器确实比当初构想的复杂,开始只打算做一个搜歌播放的功能。现在做出来的这个播放器,可以获取热门歌曲,可以搜歌,可以调整播放进度条,功能确实完善不少。

这次完成这个项目也是收获颇丰,点了不少新的技能点,当然,这个简陋的小项目也挖了不少坑,不知道啥时候能填上……

话不多说,看代码吧。

Muse-ui

不记得在哪个网站看到这个组件库的了,觉得好酷炫,于是用起来~

这是官网:地址

使用这个组件库的原因除了漂亮,还因为这是基于Vue 2.0,无缝对接,方便。

使用方法跟之前的插件一样,npm安装:

npm install --save muse-ui

安装好后,在main.js中注册。

import MuseUi from "muse-ui"
import "muse-ui/dist/muse-ui.css"
import "muse-ui/dist/theme-light.css"

Vue.use(MuseUi)

就可以在项目中使用了。
PS:Muse-ui的icon是基于谷歌的Material icons,大家可以根据自己的需求到官网找icon的代码。

组件结构

接着我们就该搭建这个播放器的组件了。

结构如下:

||-- player.vue       // 主页面
|    |-- playerBox.vue   // 播放器组件
|    |-- popular.vue    // 热门歌曲页面
|        |-- songList.vue     // 歌曲列表页面 
|    |-- play.vue    // 播放器页面
|    |-- search.vue    // 搜索页面

PS:热门歌曲、搜索页面都能进入歌曲列表页面,播放器组件playerBox.vue 是放标签的组件,是功能性组件。

我们来分别叙述:

1.player.vue

直接看代码吧:




  

解释一下:

由于Muse-ui有部分样式用到了less,所以在这里我们需要npm安装一个less的依赖,安装好后即可使用。

npm install less less-loader --save

这里我们加载了一个底部导航,muse-ui的,官网可以查到相关代码。这里要注意的是,为了让用户体验更好,我们需要让我们的底部导航随当前路由变化而高亮。具体是用了一段JS代码。

watch监视路由变化并触发一个method:changebar(),这个函数会获取当前的路由名,并把bottomNav的值设置为当前路由名——即高亮当前的路由页面

playerBox.vue组件之所以放在主组件里,就是为了音乐在每一个子页面都能播放,而不会因为跳转路由而停止播放。

2.popular.vue

这是推荐歌单界面,这里用到了一个轮播图插件,是基于vue的,使用起来比较方便,直接用npm安装:

npm install vue-awesome-swiper --save

安装好后,同样在main.js中注册:

import VueAwesomeSwiper from "vue-awesome-swiper"

Vue.use(VueAwesomeSwiper)

然后我们来看页面的代码:




  

这里要说明一下,上面的这些组件除了playerBox之外都要在main.js中注册才能使用。注册方法忘记的了话,回头看看我之前写的todolist的项目是怎么注册的。

store.js中添加playList函数:

playlist(state,id){
        const url="http://localhost:3000/playlist/detail?id="+id;
        axios.get(url).then(res=> {
            state.playlist=res.data.playlist;
        })
    },

这里的页面mu开头的基本都是用Muse-ui搭建起来的,Swiper开头的则是轮播图插件。界面不复杂,主要是三个部分,上面的轮播图,中间的热门歌单推荐,底部的版权信息。样式基本是模板,这里做了一个简单的移动端适配:在PC端歌单会以每排4个分两排的形式排列,在移动端歌单则会以每排2个分四排的形式排列,适配的方法是媒体查询,通过改变歌单div的宽度改变每行歌单的数目。

这里要注意的:

歌单的数据和轮播图都是用的网易云数据,所以没有开api是无法读取的,引入axios的部分可以先不写,也可以写好先放着。

这里methodscreated里面的内容都涉及到axios的请求,所以可以先不写,不影响样式呈现。数据可以先用假数据代替。

playList的目的是点击歌单的时候,进入歌单详情页,同时根据传递进去的歌单id获取歌单的具体数据,axios的地址是api的地址,需要加载api插件才能使用。

3.play.vue

终于到了最核心的组件,之所以说它核心是因为这是播放界面,音频播放的长度、音频信息都会在这里被呈现,而播放器的核心功能——播放——也是在这里被操作(播放/暂停)。

看具体代码:




  


store.js添加代码:

 play(state){
        clearInterval(ctime);
        const playerBar=document.getElementById("playerBar");
        const eve=$(".addPlus i")[0];
        
        
        let currentTime=playerBar.currentTime;
        let currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2);
        let duraTime=playerBar.duration;
        let duraMinute=Math.floor(duraTime/60)+":"+(duraTime%60/100).toFixed(2).slice(-2);
        state.audio.progressPercent=((playerBar.currentTime/playerBar.duration)*100).toFixed(1);
        
        if(playerBar.paused){
            playerBar.play();
            eve.innerHTML="pause";
            state.audio.duration=duraMinute;
            state.audio.currentTime=currentMinute;
            ctime=setInterval(
                function(){
                    
                    currentTime++;
                    
                    currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2);
                    
                    state.audio.currentTime=currentMinute;
                    state.audio.progressPercent=((playerBar.currentTime/playerBar.duration)*100).toFixed(1);
                    
                },1000
            )
        }else {
            playerBar.pause();
            eve.innerHTML="play_arrow";
            clearInterval(ctime);
        }
               
        
    },

    audioEnd(state){
        
        const playerBar=document.getElementById("playerBar");
        const eve=$(".addPlus i")[0];

        eve.innerHTML="play_arrow";
        clearInterval(ctime);

        
        playerBar.currentTime=0;

        let currentTime=playerBar.currentTime;
        let currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2);
        state.audio.currentTime=currentMinute;
    },

    editProgress(state,progressValue){
        const playerBar=document.getElementById("playerBar");
        const eve=$(".addPlus i")[0];

        let duraTime=playerBar.duration;
        let duraMinute=Math.floor(duraTime/60)+":"+(duraTime%60/100).toFixed(2).slice(-2);
        // console.log(progressValue);
        clearInterval(ctime);
        if(playerBar.paused){
            playerBar.play();
            eve.innerHTML="pause"
            state.audio.duration=duraMinute;
        }
        let currentTime=playerBar.duration*(progressValue/100);
        
        
        ctime=setInterval(
            function(){
                
                currentTime++;
                
                currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2);
                
                state.audio.currentTime=currentMinute;
                state.audio.progressPercent=((playerBar.currentTime/playerBar.duration)*100).toFixed(1);
                
            },1000
        )

        playerBar.currentTime=currentTime;
        
        let currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2);

        state.audio.currentTime=currentMinute;
    },

如代码所示,我在顶部导航添加了一个icon button,样式来自Muse-ui绑定了一个点击事件backpage,点击后会回到上一个路由页面。这个需要配合之前的高亮底部导航icon,才能实现返回上一路由的同时高亮相对应的icon。

还要注意的是,computed里有两个方法,第一个是获取vuex里面的当前曲目信息;第二个则是获取进度条的百分比信息,这个方法实现了数据的双向绑定,随着后台设定的计时器,不断地更新,从而实现播放时进度条的变化。同样,这里的样式也是来自Muse-uiSlider

这里有一个需要注意的坑是,Muse-ui自带了许多的函数,第一次写的时候没有注意,在进度条上绑定了一个mouseup事件,结果无效,后来才发现,其实已经自带了change事件,还可以实现移动端的兼容。所以写代码的时候一定要多看看官网文档。

关于store.js里的方法,play是播放/暂停,具体会根据当前音频文件的paused(即是否暂停)来判断。总的原理是首先获取音频的持续时间,然后通过一个定时器,不断更新显示时间,播放完成时,计时器停止。

计时器很关键,进度条和显示时间的更新都需要它。但是计时器有个坑,如果把计时器声明放在play方法里,则无法在audioEnd方法里停止计时器,所以这里我们需要在最外层先声明一个ctime,然后再在play方法里把定时器赋值给ctime,这样我们就可以随时停止计时器了。

audioEnd方法是播放停止时要做的事情,我们会把停止按钮切换成播放,把显示时间修改掉,别忘了停止计时器。

editProgress方法是点击或拖动进度条时做的事情,我们会改变当前音频的currentTime,即当前时间,如果音频是暂停状态,我们要让它继续播放。

4.search.vue

这也是一个比较核心的一个功能,毕竟推荐的歌单只有几个。看代码:




  


store.js里添加:

getSearch(state,value){
        const url="http://localhost:3000/search?keywords="+value+"?limit=30";
        axios.get(url).then(res=>{
            state.result=res.data.result;
        })
        
    },
    getSong(state,{id,name,singer,album,arid}){
        const url="http://localhost:3000/music/url?id="+id;
        const imgUrl="http://localhost:3000/artist/album?id="+arid;
        const playerBar=document.getElementById("playerBar");
        

        axios.get(url).then(res=>{
            
            state.audio.location=res.data.data[0].url;
            state.audio.flag=res.data.data[0].flag;
            
            state.audio.songName=name;
            state.audio.singer=singer;
            state.audio.album=album;
        })
        axios.get(imgUrl).then(res=>{
            state.audio.picUrl=res.data.artist.picUrl;
        })
        
        let currentTime=playerBar.currentTime;
        let currentMinute=Math.floor(currentTime/60)+":"+(currentTime%60/100).toFixed(2).slice(-2);
        let duraTime=playerBar.duration;
        let duraMinute=Math.floor(duraTime/60)+":"+(duraTime%60/100).toFixed(2).slice(-2);

        state.audio.duration=duraMinute;
        state.audio.currentTime=currentMinute;
        state.audio.progressPercent=((playerBar.currentTime/playerBar.duration)*100).toFixed(1);
        
        
    }
注意,在有需要使用axios的组件一定要import,npm下载安装不用多说了。

解释一下这个组件的两个方法:

getSearch是获取搜索结果,它被绑定再搜索按钮上,初始页面是空白,通过传递关键字,用axios从api获取搜索结果,再把结果显示在页面上。

getSong绑定在每一个搜索的结果上,有两个步骤,第一是getSong,会把点击的歌曲设置为要播放的歌曲,并把相关信息传递给play.vue,让它显示在相应的地方;第二个步骤,会播放歌曲,也就是上面的play方法,具体不必再说。

这里有一个坑,我们可能需要通过vuex传递参数,但是有时候传递多个参数会出现undefined的情况,这时候我们要把参数们写成{参数一,参数二,参数三}的形式。

5.songList

这个组件主要是歌单详情页,基本的样式和搜索页一样,就是获取歌单的内容不同,搜索页面的列表是根据关键词获取的,歌单详情页的列表是根据歌单id获取的,获取的方式都是通过axios。




  

没什么需要解释的,注意我们在getSong里面传递的多个参数。

6.playerBox.vue



  

这个页面比较简单,播放器audio标签,绑定了ended事件,即播放完成后执行。
这里有一个坑,解释一下:我把播放器按钮放在这里了,为什么呢?之前我是放在play.vue里的,但是我发现一个问题,就是通过点击歌单的歌曲播放时,无法改变播放/暂停按钮,为什么呢?因为我改变按钮的方法是用innerHTML改变,我为什么要用这种方法呢?因为Muse-ui的icon经过渲染,是以标签的值的形式出现的。这就不得不获取DOM了,但是如果把按钮写在play.vue里,在歌单页面时是获取不到指定DOM的,因为当前页面根本没有这个DOM!只有把按钮写在在主组件里的playerBox.vue里,才能获取到指定DOM。

但是写在playBox.vue里又有一个问题,按钮会出现在每一个页面里,但是我们只要它出现在播放页面就好了,所以我们在这里要给按钮绑定一个v-show,里面的内容就是判断是不是在指定路由,如果是播放页面,就显示按钮,不是,就隐藏按钮。

axios和网易云api

axios具体的配置我都在上面讲了,这里介绍一款网易云的api和使用方法。

文档在此

介绍一下使用方法,进入git把它下下来,在命令行执行:

$ node app.js

在浏览器输入地址:

localhost:3000

看到弹出的页面就说明服务器启动成功了。然后我们可以在文档里查到具体请求的数据,比如banner啊,歌单啊,搜索啊,都能请求。我们看到前面写的axios请求里的地址,都是具体请求的地址。

这里要注意的是,这个api默认的是没有开启跨域的,看app.js里有一段被隐藏的代码就是跨域的相关设置,解除隐藏即可。

bug和未实现功能

目前还存在一个比较大的bug,就是在歌单点击播放时,点击第一次因为没办法获取个去的url,无法播放,只有再点击一次才能播放,这个bug暂时还没有时间解决,会尽快解决。

然后目前还没有实现的功能是播放列表,自然上一曲/下一曲按钮也没有用了,歌曲播放一遍也就停止了,这个功能不算难,抽空把它做出来。

参考资料

这个app参考了一些技术文章,给了我很大的启发,附上链接。
用vue全家桶写一个“以假乱真”的网易云音乐
DIY 一个自己的音乐播放器 2.0 来袭

结语

这个app前前后后,磨磨蹭蹭做了两个月,好歹总算是做完了。学习还是得找项目来做,虽然这个项目还很简陋,但是还是get到很多知识点,对于我的提高还是蛮大的。

这种项目不算难,写过的人也多,所以百分之八十的问题都能百度出来,剩下的百分之二十,技术社区里提个问基本能够解决。项目还是得自己写一遍,写的过程中才能发现问题,也才能想办法找到解决办法,事情总是会比你想象的要简单一点。

项目不算大,但要一步步写下来总有可能有所遗漏,这里是我的GitHub,大家可以对照着看看有没有遗漏。如果你喜欢我的项目,也希望star或者fork一波~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107099.html

相关文章

  • Vue 实现网易云音乐 WebApp

    摘要:基于等开发一款移动端音乐,界面参考了安卓版的网易云音乐布局适配常见移动端。图标使用阿里巴巴图标库,中间的唱片旋转动画使用了实现。搜索功能实现功能搜索歌手歌单歌曲热门搜索数据节流上拉刷新保存搜索记录。 基于 Vue(2.5) + vuex + vue-router + vue-axios +better-scroll + Scss + ES6 等开发一款移动端音乐 WebApp,UI ...

    Karuru 评论0 收藏0
  • Vue 写出好看又好音乐放器 - Vue-APlayer

    摘要:好看又好用的,专为以为原型,在技术栈上进行实现。项目早在就已起步,起初是对的简单封装。现仍在持续维护和更新中。如果你在使用搭建自己心爱的小站,正想挑选一款好看又好用的音乐播放器,是少数不错的选择。 Vue-APlayer showImg(https://segmentfault.com/img/remote/1460000013797187); showImg(https://segm...

    tomato 评论0 收藏0

发表评论

0条评论

appetizerio

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<