代码改变世界

HTML5音乐播放器

2011-04-15 00:00  Feather  阅读(661)  评论(0)    收藏  举报

 前几天都在研究HTML5的媒体标签Audio和Video,本来打算写写笔记的,但把文章提纲列了出来后,又觉得如果展开来写的话内容实在太多了,于是,还是觉得编写一个Demo好了。

还是先上Demo,还是那句话,请使用Chrome浏览,Safari也可以。刚刚发现iphone也可以完美支持。

 

基本思路

这个播放器构思了两天,很多细节一直确定不了,很多想法都是Coding的时候爆发出来,然后临时加上去的,虽然是这样,但程序都写得很顺利,没有被什么问题卡住,整个播放器用了半个下午和半个晚上搞定。

目前播放器还很简陋,用户需要听音乐可以直接在输入框中写入歌曲,或者歌手,专辑之类的都可以。点击“我要听这首”,浏览器会发请求到服务器端,服务器到Soso音乐里面抓取,抓取的信息包括歌名,歌手,专辑,以及两个MP3地址。数据交给前端后,就自由发挥了。

一开始的想法是音乐控制栏也自己弄的,这样可以在各个浏览器中统一样式,而且控制可以更加灵活。但是,我最终还是用了默认的控制栏,主要原因还是工程量太大了,换句话说就是我懒。

Audio的API很丰富,很给力,播放器各个部件可以和它很好的衔接。

最后要说说设计,这次的设计我很喜欢,基本符合了一开始的想法。因为CSS3提供了很好透明度,投影的支持,这让我们实现简单透明质感效果变得很简单,这个设计只有一张图片,就是背景图,是我去Los Angeles飞机上拍的。

 

代码分享

一开始在纠结,播放器应该写成一个构造函数呢,还是原型对象,抑或是普通对象。这个问题我以后要研究一下,基于构造函数,原型对象,对象的编程,各有什么利弊。反正我这次我直接构造一个Player对象,毕竟我目前觉得还没有需要实例化多个播放器,或者要继承之类的。就算以后需要,只要新建一个构造函数,并把它的原型指向我现在编写的这个Player对象就好了。

var Player = {
        playList: [{ title: 
"天空之城", singer: "久石让", URL: ["http://upload20.music.qzone.soso.com/30479511.mp3","http://landodesign.net/sky.mp3"]},
                        {title: 
"金鱼的眼泪", singer: "纪佳松", URL: ["http://wma.9ku.com/2011/3/26/1.mp3"]}],//播放列表数据
        currentSong: {},//当前播放歌曲
        audio: document.getElementById("audio"),//绑定页面相应的Dom,下面几个都是
        list: document.getElementById("playlist"),
        search: document.getElementById(
"btn"),
        keyword: document.getElementById(
"search"),
        state: document.getElementById(
"state"),
        init: 
function () {//初始化函数
            this.renderList();//渲染播放列表
            var self=this;
            
this.search.onclick = function () { Player.add(self.keyword.value); }//绑定点击搜索歌曲的按钮
        },
        add: 
function (keyword) {//添加歌曲函数,参数是关键字
            var self = this;
            
this.search.innerHTML = "我挖啊挖...";
            
var xhr = new XMLHttpRequest();//初始化XHR对象,下面是发送数据和一些准备工作
            xhr.open("GET""info2.ashx?keyword=" + window.escape(keyword), true);
            xhr.onreadystatechange 
= function () { if (xhr.readyState == 4) callback(xhr.responseText); }
            xhr.send(
null);
            
function callback(data) {//回调函数
                if (data) {//如果有数据传回来
                    var obj = eval(data);//解析为对象并且push进播放列表中
                    self.playList.push(obj);
                    self.renderList();
//渲染列表
                    if(self.audio.src==""||self.audio.ended)self.play(obj);//如果是之前没有播放或者已经结束了,就直接播放搜索出来的那首歌
                }
                
else {//如果搜不到或者出错都会返回空字符串,然后会有这个提示
                    alert("木有这首歌啵~");
                }
                self.search.innerHTML 
= "再来一首吧";
            }
        },
        play: 
function (song) {
            
var self = this;
            
this.audio.src = song.URL[0];//把第一个URL赋给audio
            this.audio.addEventListener("error"function () {//如果出错了并且有两个URL,就尝试第二个;下面是都各种事件的绑定
                if (self.audio.src == song.URL[0&& song.URL[1]) self.audio.src = song.URL[1];
                
else self.changeState("链接失败!");
            });
            
this.audio.addEventListener("waiting"function () {
                self.changeState(
"等待缓冲...");
            });
            
this.audio.addEventListener("playing"function () {
                self.changeState(
"正在播放 :" + song.title + " - " + song.singer, "正在播放 :" + song.title);
            });
            
this.audio.addEventListener("pause"function () {
                self.changeState(
"暂停播放 :" + song.title + " - " + song.singer, "正在播放 :" + song.title);
            });
            
this.audio.addEventListener("ended"function () {//如果播放完了,就随机播放下一首,不知道是不是算法有问题,感觉很不随机
                self.play(self.playList[Math.ceil(self.playList.length * Math.random()) - 1]);
            });
            
this.audio.play();//播放
            this.currentSong = song;
            
this.renderList();
        },
        renderList: 
function () {//渲染列表函数
            var self = this;
            
this.list.innerHTML = "";//先清空Dom
            for (var i = 0; i < this.playList.length; i++) {//一个个的节点加上去
                var a = document.createElement("a");
                
if (this.playList[i] == self.currentSong) a.setAttribute("class""playing");
                a.innerHTML 
= this.playList[i].title + " - " + this.playList[i].singer //+ (this.playList[i] == self.currentSong ? "  [正在播放...]" : "");
                a.href = "javascript:void(0)";
                a.onclick 
= (function (song) {//这里要创建闭包
                    return function () {
                        self.play(song);
                    }
                })(
this.playList[i]);
                
this.list.appendChild(a);
            }
        },
        changeState: 
function (info, title) {//改变状态函数
            this.state.innerHTML = info;//这个是控制栏上面的哪里
            document.title = title || info;//这个是网页标题,如果没有第二参数则使用前一个参数
        }
    }
    window.onload 
= Player.init();

 

 未来的改进

做完这个蛮有成就感的,还打算以后继续完善一下。下面是目前的一些想法:

  1. 兼容性问题。这个问题很蛋疼,IE和FF都不支持的确不太好。我目前比较倾向两种兼容方案:第一种是嵌入Flash,在非Chrome和Safari浏览器中调用Flash来播放音乐,但是这样子就把播放器回归到传统了,也不能叫HTML5播放器了,这样意义不大。另外是编码兼容方案,每一首歌抓取的时候把两种编码的地址都拿下来,根据不同浏览器播放不同的。这种方法就直接无视了不支持HTML5的浏览器,但是我觉得最大问题是:去哪里抓取ogg格式啊?
  2. 使用模式问题。现在用户要听音乐,唯一办法就是输入关键字,然后歌曲就自动列入播放列表。这种使用模式太麻烦了,我也想过很多模式,譬如:默认载入一些新歌在列表中,或者好像豆瓣虾米那样可以随便听听,还是完全由用户自己管理,然后还有把歌曲信息保存在客户端或者云端。

呃……目前就想到这些,欢迎大家给点意见。

 

参考资料

W3school Audio

WHATWG Audio

HTML5 Audio/Video 标签,属性,方法,事件汇总