最近准备把之前做的一个仿网易云音乐的自制音乐网页播放器项目做一个总结。
相关功能如下:
-
通过后台页面上传歌曲、编辑歌曲功能。
-
前端页面自动更新播放热度高的歌曲
-
在线听歌、查看歌词。且配有相应的播放动画。
预览链接:https://leonardo-zyh.github.io/163-music-demo/src/index.html
可通过微信二维码打开:
该项目主要是使用了jQuery以及MVC模块化的思想来完成的移动端音乐会播放器,因此在介绍这个应用的制作思路和流程之前,我想重新总结一下对模块化和MVC的理解。
模块化
我的认识中模块化是通过MVC的V,也就是View来划分的,把页面中看得见的区域进行功能划分,每一个功能不同的区域就是一个分开的模块,
window.eventHub={
events:{},
emit(eventName,data){
for(let key in this.events){
if (key===eventName){
let fnList=this.events[key]
fnList.map((fn)=>{
fn.call(undefined,data)
})
}
}
},
on(eventName,fn){
if (this.events[eventName]===undefined){
this.events[eventName]=[]
}
this.events[eventName].push(fn)
}
}
MVC模式
MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:
模型(Model)、视图(View)和控制器(Controller)
- 模型(Model)
模型层:数据保存,可以简单理解就是数据层,用于提供数据。在项目中,(简单理解)一般把数据访问和操作,比如将对象关系映射这样的代码作为Model层,也就是对数据库的操作这一些列的代码作为Model层。比如代码中我们会写DAO和DTO类型的代码,那这个DAO和DTO我们可以理解为是属于Model层的代码。- 视图(View)
视图层:用户界面,就是UI界面,用于跟用户进行交互。一般所有的JSP、Html等页面就是View层。- 控制器(Controller)
控制层:业务逻辑,Controller层的功能就是将Model和View层进行关联。比如View主要是显示数据的,但是数据又需要Model去访问,这样的话,View会先告诉Controller,然后Controller再告诉Model,Model请求完数据之后,再告诉View。这样View就可以显示数据了
render()
函数来更新功能区的HTML代码,使页面视图发生相对应的改变。总的来说,View要做的事情,就是改变网页的用户视觉,去把代码改变的内容以直观的方式呈现在网页中,需要切记的是每个MCV模块中View所代表的区域之间是不能互相交叉的,因此在进行模块化时要明确好每个模块的职责。let view={
el:'#用户界面',
template:`
<div>HTML模版</div>
`,
init() {
this.$el = $(this.el)
},
render(){
$(this.el).html(this.template)
}
}
let model={
data:{},
fetch(){......},
save(){......},
update(){......}
}
Controller作为业务逻辑,Controller层的功能就是将Model和View层进行关联。Controller代表的是控制当前模块在不同的时刻所进行的操作,比如,Controller对象里一般都会有init方法、bindEvents方法和bindEventHub方法。init方法意思就是在模块初始化的时候,需要做些什么,因此我们会在init方法里面初始化view、初始化model,进行事件绑定,进行事件发布订阅中心的事件订阅等等,这些就是我们在模块初始化时要做的事情。然后在元素触发事件时模块需要做什么,在其他模块发布事件后模块需要做什么,都分别反映在了Controller的bindEvents方法和bindEventHub方法中,因此Controller就像一个控制塔,有条不紊地在合适的时候处理着合适的事情,是统筹Model和View的中心。
let controller={ init(view,model){ this.view=view this.model=model this.bindEvents() this.bindEventHub() }, bindEvents(){......}, bindEventHub(){......} }
相信在你看完以上我对模块化和MVC的项目总结之后,会帮助你更好地梳理接下来我要介绍的音乐播放器的思路。因为这个应用涉及的代码很多,所以我只能介绍重要的思路,以及会在最后说一下在制作过程中遇到的几个坑、问题以及解决这个问题的思路的做法。
项目的制作思路
当你有了制作某个项目的想法,你第一件要做的事情是什么,不是直接写代码,而是分析这个项目,我们可以通过以下三个图例来进行分析:
1.用例图(use cases)
分析当你身为用户或者应用管理员的时候,使用应用的时候需要什么的页面,页面需要怎么样的功能,这就是用例图会表达出来的内容。
例如音乐播放器这个应用,身为普通用户的话,我们可以查看首页、查看歌单页和歌曲页,歌曲页里面可以听歌、可以暂停以及可以查看歌词,我们还可以搜歌,可以搜歌来搜出歌手和歌曲名等等。
通过这些分析,你就会了解到你当前要制作的这个应用,他需要怎么样的功能以及每个功能应该出现在哪一个页面当中。
2.线框图(也叫草图,stretch)
线框图要展示的,就是你要制作的应用中,每个页面功能区的布局,也就是线框图会告诉你这个应用含有多少个页面,每个页面里有着哪些功能区,以及功能区的大体位置也能在上面体现出来。
3.系统架构图
系统架构图展示的是在该应用中,前端页面、后端页面以及数据库中使用的是什么工具,例如音乐播放器中,前端页面使用的是jQuery,后端页面使用的是LeanCloud提供的API,数据库使用的是 LeanCloud和七牛,以及这三者之前的交互方式,例如前端页面和后端页面的交互方式是通过AJAX来进行的。
关于音乐播放器的样式问题,这是需要自己去花时间去寻找优秀的设计模板,并进写模仿、修改和编写才能得到的内容,因此就不在这里进行阐述了。
关于项目中所遇到的问题
在进行音乐作品播放器的制作过程中,我在一些地方使用了ES6的新语法展开语法
...
,这个语法的表示的是当前对象的所有属性,如:
let attributes={name:"xzb",age:18}
let obj={id:1,...attributes}
console.log(obj) // {id:1,name:"xzb",age:18}
这个语法在是【使用中很方便,但是当你在移动端使用它的时候,很容易出现语法错误,移动端不支持
后面只能使用Object.assign方法来代替它了。
解决办法:使用Object.assign()
2.无法对移动端进行调试
第二个坑是由第一个坑衍生出来的,在发现第一个坑之后,我的第一个反应是,十分无奈,为什么这么说呢?哥,你在PC端出错,我还能通过控制台来看看出错的地方在哪,你在移动端出错,我.....
好吧,只能靠万能的互联网了,在一番资料的查询之后,我得到了想要的解决办法,有以下四个:
(1)通过alert()来进行检验
虽然说移动端没有控制台,但移动端还是可以alert的吖,因此我们只要在我们认为出错的地方的前后进行alert(),若发现前面的alert运行了,后面的alert没有运行,那么恭喜你,你的猜测是正确的,出错的地方就是此处。通过这种办法我们就可以在移动端知道自己出错的地方了。
(2)通过全局的onerror来进行检验
通过第一种方法我们的确可以在不断的尝试下知道出错的地方,但是这样效率太低下了,于是我们有第二种办法,通过监听全局的error来显示错误,以及显示错误的出处。代码如下:
<script>
window.onerror=function(message,file,row){
alert(message,file,row)
}
</script>
onerror接受四个参数,第一个是出错信息,第二个出错文件,第三个是出错的行数,第四个是出错的列数,由于列数在此处对我没什么太大的作用,因此在上述代码中我把它省略了。
(3)自己手写一个console函数法
自己在页面上写一块console的区域,然后把console的值直接显示在区域内即可,具体代码如下:
<div id="consoleOutput" style="......"></div>
<script>
window.console={
log(x){
let p=document.createElement('p')
p.innerText=x
consoleOutput.appendChild('p')
}
}
</script>
(4)直接引入腾讯制作的vConsole库
直接引入腾讯制作的vConsole库,就可以在移动端拥有一个console了,但是需要记住在调试完之后记得删掉这些调试工具,以免出现在用户使用的页面中。
注意:http-server局域网调试,建议关闭防火墙
3.由于IOS的移动端不支持animation-play-state语句而导致的BUG。(注:IOS12已修复)
在我对移动端中的所有报错都进行了修复之后,我在歌曲播放页面又发现了一个新的BUG,那就是在点击光盘进行播放的时候,光盘没有如我代码所写那样进行播放,百思不得其解之下,我发现网上有许多人和我出现了类似的BUG,于是乎我就去寻找出现这种现象的原因,以及解决的办法,最后我发现原来是IOS不支持animation-play-state语句而导致的,而在安卓的移动端上则不会出现这样的BUG。
如何是好,没有了animation-play-state,我就无法去控制CSS3动画的播放和暂停,如果单纯的用animation:none;
来控制,就会出现动画每次都重头开始进行的错误效果,后来我也尝试了animation-fill-mode: forwards;
来尝试让动画每次停在最后的状态,但由于歌曲的暂停是随机的,而不是由动画是否播放完毕来决定是否暂停的,因此这个方法也是行不通的,怎么办好。我不断的尝试,不断地搜索资料,最后我看到了一个网上的解决方案:给光盘所在元素添加一个父元素,当每次点击光盘暂停歌曲播放时,用父元素记录一下光盘每次暂停时transform的值,并让父元素的transform也等于这个值,若transform本来就有值,那就在transform后面更新这个值,就可以完成歌曲暂停,光盘动画暂停,歌曲继续开始,光盘动画继续开始的效果。
具体的实现代码如下:
recordTransform(){
let coverTransform=this.view.$el.find('.coverWrapper').css('transform')
let coverWrapperTransform=this.view.$el.find('.coverWrapperParent').css('transform')
let transformDeg=coverWrapperTransform==='none'?coverTransform:coverTransform.concat(' ',coverWrapperTransform)
this.view.$el.find('.coverWrapperParent').css('transform',transformDeg)
}
4.由于IOS微信不支持webp格式图片所引起的BUG
由于我的音乐播放器中的歌单用的是webp文件的封面图片,这个格式的图片是谷歌开发的一种旨在加快图片加载速度的图片格式。WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。
可惜IOS上的微信就是不支持这种格式的图片,因此没有办法,我最后解决这个BUG的方案就是把webp图片全部转换成了JPG然后重新上传至应用中。
解决办法:更换应用图片格式
终于算是介绍完了如何仿照网易云音乐自制一个音乐网页播放器,当我做完这个应用的时候,我觉得自己对MVC的理解和使用都更为熟练了,接下来我会完成Vue相关的应用,并继续给大家带来完成后的心得和感想,希望能对你们有所帮助~