资讯专栏INFORMATION COLUMN

vue——一个页面实现音乐播放器

Java3y / 1213人阅读

摘要:用做进度条本身的样式很难看,并且不同的浏览器呈现出来的效果也不一样。背景滤镜模糊将图片设置为背景的感觉很棒,可以说整个播放器的颜值这背景提供了一半。正确写法错误写法待优化手动修改进度,偶尔会不生效。


请忽略下面这段文字
年关将至,时间好歹又多出了些许。却不敢过度消遣。岁月未曾饶过我,我亦不想饶过岁月。且将它塞得膨胀,让这一年看似加更充实。
不曾料想我一个爱些风花雪月、研墨行歌之人,却做起了碼农这一行当。虽做了碼农,却断不了嗜好,日日必听歌。奈何近日,公司限制高流量网络访问。虾米、网易云、QQ甚至酷狗,无一幸免。
没有了歌,变得像挨了锤的牛一样,疲软成一滩烂泥。
所幸还能用用listen 1,可这listen 1却让人味同嚼蜡。歌且无过,想是这界面不合胃口。做一名前端,也沾了对外观挑三检四的毛病。
于是灵光一闪,便想造一个出来。


项目地址:https://github.com/ermu592275...
演示地址:https://ermu592275254.github....(因referer限制,无法搜索歌曲)

开发前构思 界面

做音乐播放器,界面一定要炫酷。太low了听歌没感觉。本身是为了在上班的时候用,于是做成了一个类似网易云音乐的界面,大小合适。不用兼容手机端。

用css做图标

本怀着简单实用的需求去考虑,图标可用SVG、url或者css。相对于url,SVG和css更好一些。为了修炼,最终选择的css。利用好after和before,能减少很多dom嵌套。

 .next {
    position: relative;
    display: inline-block;
    height: 36px;
    width: 36px;
    border: 2px solid #fff;
    border-radius: 20px;
    -webkit-border-radius: 20px;
    -moz-border-radius: 20px;
}
        
.next:before {
    content: "";
    height: 0;
    width: 0;
    display: block;
    border: 10px transparent solid;
    border-right-width: 0;
    border-left-color: #fff;
    position: absolute;
    top: 8px;
    left: 10px;
}

.next:after {
    content: "";
    height: 20px;
    width: 4px;
    display: block;
    background: #fff;
    position: absolute;
    top: 8px;
    left: 22px;
}
画一个唱片

网易云的唱片很好看,我也要弄一个唱片!
用好box-shadow,一个元素便可以做成很好看的唱片效果。

.disc {
    position: relative;
    margin-top: 10%;
    margin-left: 10%;
    width: 300px;
    height: 300px;
    border-radius: 300px;
    transform: rotate(45deg);
    background-image: radial-gradient(5em 30em ellipse, #fff, #000);
    border: 2px solid #131313;
    box-shadow: 0 0 0 10px #343935;
    opacity: 0.7;
}
用range做进度条

audio本身的样式很难看,并且不同的浏览器呈现出来的效果也不一样。当然你可以修改audio的样式,传统的做法是通过controls属性来隐藏audio,然后用div来代替。现在是html5时代了,当然要用更符合场景的新元素————range。

 input[type=range] {
    -webkit-appearance: none;
    width: 80%;
    height: 8px;
    border-radius: 10px;
    background-color: #fff;
}
input[type=range]::-webkit-slider-thumb{
    -webkit-appearance: none;
} 
input[type=range]::-webkit-slider-runnable-track {
    height: 8px;
    border-radius: 20px;
}
input[type=range]:focus {
    outline: none;
}

input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    margin-top: -3px;
    height: 14px;
    width: 14px;
    background: #eb7470;
    border-radius: 50%;
    border: solid 3px #fff;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.5);
}
背景滤镜模糊

将图片设置为背景的感觉很棒,可以说整个播放器的颜值这背景提供了一半。设置也非常简单,用到了css3的滤镜。

.bg-blur {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
    height: 100%;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    filter: blur(20px);
    z-index: -1;
}

背景图片通过js控制。

 
歌曲资源 爬下接口

直接去虾米官网打开network,将url复制到postman里面去做请求。通过修改headers发现,会校验Referer。也就是说只有虾米允许的域名可以访问此接口。
http://api.xiami.com/web?v=2....

解决跨域问题

因为接口支持jsonp。起初尝试将chrome浏览器设置跨域,然后通过$.ajax去做一个jsonp的请求。可以正常访问。
之后突然不行了,是不是虾米做了限制?
遂改用node启动一个服务,去伪造referer发起请求,再将请求转发到页面。无意中写了一个代理。

...
case "/song":
    let songOptions = {
        url: "http://api.xiami.com/web?"+ urlArr[1],
        headers: {
            "Referer": "http://m.xiami.com/"
        }
    };
    function callback1(error, response, body) {
        if (!error && response.statusCode == 200) {
            res.end(body);
        }
    }
    request(songOptions, callback1);
    break;
...
歌词滚动

作为一款逼格比较高的播放器,歌词滚动是必须的。

原理

将每一句歌词保存为一个对象,有对应的时间。当歌曲播放的当前时长大于或等于歌词的时间,小于此歌词的下一句歌词的时间,那么就将此歌词滚动到可视区域。并且修改字体颜色。

格式化歌词

接口返回的歌词一脸懵逼,仔细研究一下,发现是有规律的。

[ti:aLIEz]
[ar:SawanoHiroyuki[nZk]:mizuki]
[al:o1]
[ly:澤野弘之]
[mu:澤野弘之]
[ma:]
[pu:]
[by:ttpod]
[total:268512]
[offset:0]
[00:00.000]<195>aLIEz <199>- <451>SawanoHiroyuki[nZk]:mizuki
[x-trans]彻头彻尾的谎言 - SawanoHiroyuki[nZk]:mizuki
[00:01.095]<201>作<250>詞<200>:<201>澤<200>野<199>弘<300>之
[x-trans]
[00:02.846]<200>作<150>曲<150>:<200>澤<200>野<351>弘<349>之
[x-trans]
[00:20.828]<200>決<250>め<200>つ<201>け<149>ば<201>か<349>り
[x-trans]一直独断专权
[00:23.279]<200>自<200>惚<200>れ<200>を<200>着<400>た
[x-trans]总是自负逞强
[00:24.979]<200>チ<200>ー<200>プ<450>な<550>hokori<350>で
[x-trans]明明只是一文不值的骄傲
......
   refactoringLyrics(lyric){
    let text = lyric.split("[offset:0]")[1];
    let textArr = text.split("
");
    let lyricsArr = [], translate = [];
    textArr.forEach((item, index) => {
        let time = 0, text = "";
        if (item.indexOf("[x-trans]") > -1) {
            translate.push(item.split("[x-trans]")[1])
        } else if (item.trim() != "") {
            time = item.slice(1, 6).split(":");
            time = parseInt(time[0]) * 60 + parseInt(time[1]);
            text = item.slice(11);
            let arr = text.split(">");
            let str = arr.reduce((a, b) => {
                return a.split("<")[0] + b.split("<")[0]
            });
            let obj = {
                time: time,
                text: str
            };
            lyricsArr.push(obj);
        }
    });
    for (let i in translate) {
        lyricsArr[i].text = lyricsArr[i].text + "
" + translate[i];
    }
    this.currentLyrics = lyricsArr;
},
搜索栏实现 同文件下子组件挂载

为了遵循模块化开发,决定将搜索栏写成一个子组件。在同一页面下写子组件,子组件挂载到对应的template就有讲究了。此template不能被父组件的挂载元素包含,否则父组件渲染时会因为无法渲染子组件中的数据而报undefined。

...
var searchBox = { template: "#search-box", props: { isShow: Boolean, openFun: Function }, data(){ return { resultList: [], searchValue: "", } }, methods: { } }; new Vue({ el: "#app", components: { "com-tip": tip, "search-box": searchBox }, ... })
eventBus解决数据传输

通过jsonp去请求数据,需要设置一个callback函数,此callback写成一个全局函数,如果不这样写,而是通过
searchBox.methods.callback的形式,this指向将为methods。而无法直接给searchBox的data赋值。
于是通过eventBus来处理,这样更易维护。

var EventBus = new Vue();
var callBack = function(result) {
    console.log(result);
    EventBus.$emit("callBack", result);
};
...
mounted(){
    let self = this;
    EventBus.$on("callBack", function(res) {
        if (res && res.data) {
            self.resultList = res.data.songs;
        }
    })
}
...
localStrong储存歌曲信息

下次再打开,应该播放列表应该保留上一次的数据,这个可直接用localstrong实现

技术补充

滤镜模糊 http://www.zhangxinxu.com/wor...
css图标 http://www.uiplayground.in/cs...
flex 兼容写法: https://www.cnblogs.com/irisz...
flex 布局教程 http://www.ruanyifeng.com/blo...
滑动条样式自定义 http://blog.csdn.net/u0133472...
隐藏滚动条兼容 https://segmentfault.com/q/10...
audio属性 https://developer.mozilla.org...
audio事件 https://developer.mozilla.org...

踩了坑 prop传递数据

使用cdn,vue的prop只支持中线格式,驼峰格式不生效
ps: 在用webpack打包的项目中用驼峰是可以,在打包过程中,会做处理。

// 正确写法

// 错误写法
待优化

手动修改进度,偶尔会不生效。
搜索暂不支持分页
不支持建歌单
背景颜色与进度条颜色相近需修改进度条颜色
不支持播放模式选择-单曲循环-随机播放

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

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

相关文章

  • Vue 实现网易云音乐 WebApp

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

    Karuru 评论0 收藏0
  • vue.js 移动端音乐app(一) 基础组件 scroll

    一、 基础实现 (1)功能 对 better-scroll 插件的基本封装,实现移动端的滚动 (2)实现 引入 better-scroll props probeType: better-scroll 配置项之一 (1)取值: 1 滚动的时候会派发 scroll 事件,会截流。 2 滚动的时候实时派发 scroll 事件,不会截流。 3 除了实时派发 scroll 事件,在 swipe 的情况...

    wqj97 评论0 收藏0
  • vue.js 移动端音乐app(一) 基础组件 scroll

    一、 基础实现 (1)功能 对 better-scroll 插件的基本封装,实现移动端的滚动 (2)实现 引入 better-scroll props probeType: better-scroll 配置项之一 (1)取值: 1 滚动的时候会派发 scroll 事件,会截流。 2 滚动的时候实时派发 scroll 事件,不会截流。 3 除了实时派发 scroll 事件,在 swipe 的情况...

    FingerLiu 评论0 收藏0
  • vue.js 移动端音乐app(一) 基础组件 scroll

    一、 基础实现 (1)功能 对 better-scroll 插件的基本封装,实现移动端的滚动 (2)实现 引入 better-scroll props probeType: better-scroll 配置项之一 (1)取值: 1 滚动的时候会派发 scroll 事件,会截流。 2 滚动的时候实时派发 scroll 事件,不会截流。 3 除了实时派发 scroll 事件,在 swipe 的情况...

    MadPecker 评论0 收藏0
  • ♥基于H5+js开发一款音乐放器

    前言:当下音乐播放器不胜其数,为了更好的掌握一些东西,我们来自己制作一个音乐播放器。 文章目录: 一.开发环境:二.页面视图:1.主文件入口(首页):2.音乐播放界面: 三.功能实现(1)、index.html:(2)、播放音乐(music.html):(3)、样式文件(index.css): 四.项目地址: 一.开发环境: 开发工具:HbuliderX; 框架:Vant,Mui,V...

    BearyChat 评论0 收藏0

发表评论

0条评论

Java3y

|高级讲师

TA的文章

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