以前一個前端羣裏 大牛 作了一個自適應的HMLT5播放器 javascript
最近根據其思路作了一個相對單一移動端的demo,demo用的圖片和歌曲json的數據設計 都是羣裏大牛作的,在這謝謝~;css
同時借鑑的幾篇文章:html
慕課網 : http://www.imooc.com/view/299 (這個我也沒看完....)前端
leinov : http://www.cnblogs.com/leinov/p/3896772.html (完整的音頻的解釋demo)java
陳在真 :http://blog.sina.com.cn/s/blog_74d6cedd0102vkbr.html (也是這篇文章 讓我在此次demo 裏沒有作音量控制和這個音量漸進、減退,由於感受 沒多大用....尤爲是在手機端都有這個音控的按鍵前提下....)ios
上圖:web
上代碼:json
HTML:api
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>HTML5 播放器</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="description" content=""> <meta name="apple-touch-fullscreen" content="yes" /> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" /> <meta content="no" name="apple-mobile-web-app-capable" /> <meta content="black" name="apple-mobile-web-app-status-bar-style" /> <meta content="telephone=no" name="format-detection" /> <style> *{margin:0;padding:0;} li{ list-style: none;} a{ text-decoration: none;} html,body{width:100%; height:100%; font-family: 'microsoft yahei', Arial, Helvetica, sans-serif;} body{-webkit-tap-highlight-color: rgba(0,0,0,0); } .tac{ text-align:center;} .fl{ float:left;} .fr{ float:right;} .body_bg{transition:opacity 0.5s ease-in; position:absolute;left:0;top:0; width:100%; height:100%; background-image:url(bg.jpg) 0 0 no-repeat; background-size: cover; opacity: 0;} .body_bg.cur{opacity:1;} .list_main{overflow-y: auto;} .list_ul{ width:100%;} .list_ul li{ position:relative; min-height: 55px; font-size: 14px; padding:0 20px; color:#fff; line-height: 20px;} .list_ul li.cur{ color:#f48e4a;} .list_line{ position:absolute;left:0;bottom:0; width:100%; height:1px; background: rgba(0,0,0,.3);} .list_ul .music_mian{ position:absolute;top:50%;transform: translateY(-50%);} .list_ul p{ font-size: 14px;} .audio_main{ position:fixed; bottom:0;left:0; width:100%; height:100px;background:rgba(0,0,0,.3); } .process_line{ position:relative; width:100%; height:3px; background-color: #fff; } .progress_after{ position:absolute;left:0;top:0; width:0px; height:3px; background-color: #f48e4a;} .process_main{ } .music_time{ color:#fff; font-size: 12px;margin:5px;} .music_btn{ position:absolute;bottom:0;left:50%; width:238px; transform: translateX(-50%);} .music_btn a{ display:inline-block; width:60px; height:46px; background:url(player.png) no-repeat 0 0; font-size: 0;} .music_btn .music_before{background-position:-143px 0; } .music_btn .music_switch{ background-position:-68px 0;margin-right:20px;} .music_btn .cur{ background-position:-68px -140px;} .music_btn .music_after{background-position:0px 0;margin-right:20px;} .name_singer{ color:#fff; margin:5px; font-size:12px;} .audio_dot{ position:absolute;left:0;top:-6px; width:14px; height:14px;border-radius: 50%; background:#fff;} .audio_Bgdot{ position:absolute;left:-15px;top:-15px; width:44px; height:44px;background:rgba(0,0,0,0);} .turn{ position:absolute;left: -36px;bottom: 12px; width:30px;height:30px; text-align:center;line-height: 30px; font-size: 14px; color:#fff;} </style> <script type="text/javascript" src="zepto.js"></script> <script type="text/javascript" src="music.js"></script> <script type="text/javascript"> $(function(){ var audios = [ { name:'知己', singer:'未知', src:'02.mp3', cover:'' }, { name:'暗號', singer:'周杰倫', src:'01.mp3', cover:'cover.jpg' }, { name:'One Day.mp3', singer:'Kodaline', src:'03.mp3', cover:'' }, { name:'KOK', singer:'未知', src:'KOK.mp3', cover:'' }, { name:'一塊紅布', singer:'', src:'04.mp3', cover:'' }, { name:'Hall Of Fame ', singer:'The Script Feat Will.I.Am', src:'05.mp3', cover:'' }, { name:'Rumour Has It', singer:'未知', src:'06.mp3', cover:'' }, { name:'尋找', singer:'李志', src:'08.mp3', cover:'' }, { name:'Rooftops', singer:'Kris Allen', src:'07.mp3', cover:'' } ]; Music.init(audios); }); </script> </head> <body style="background-image:url(bg.jpg); opacity:1; "> <div class="body_bg" style="background-image:url(bg.jpg); opacity:1; "></div> <div class="list_main" id="list_main"> <ul class="list_ul" id="list_ul"> </ul> </div> <div class="audio_main" id="audio_main"> <div class="process_main"> <div class="process_line" id="process_line"> <div class="progress_after" id="line_after"></div> <div class="audio_dot" id="dot"> <div class="audio_Bgdot"></div> </div> </div> </div> <div class="fl music_time" id="time_start">00:00</div> <div class="fr music_time" id="time_end">00:00</div> <div class="tac name_singer" id="name_txt"></div> <div class="tac name_singer" id="singer_txt"></div> <div class="music_btn"> <a href="javascript:;" class="music_after" id="music_after"></a> <a href="javascript:;" class="music_switch" id="music_switch"></a> <a href="javascript:;" class="music_before" id="music_before"></a> <div class="turn" id="turn">順</div> </div> </div> </body> </html>
js:瀏覽器
var Music = { init : function( data ){ this.data = [];//存儲數據; this.randomArr = []; for( var i=0,len=data.length;i<len;i++ ) { if( data[i].src ){ this.data.push( data[i] ); this.randomArr.push(i); } } this._getElement(); //獲取dom; this.createAudio()//建立音頻; this.createMusicList(); //建立歌單; }, _getElement : function(){ this.list_ul = $('#list_ul'); this.music_switch = $('#music_switch'); this.music_before = $('#music_before'); this.music_after = $('#music_after'); this.time_start = $('#time_start'); this.time_end = $('#time_end'); this.process_line = $('#process_line'); this.line_after = $('#line_after'); this.window = $(window); this.bodybg = $('.body_bg'); this.dot = $('#dot'); this.turn = $('#turn'); this.trunNum = 0; this._dot = document.getElementById('dot'); this._process_line = document.getElementById('process_line'); this.Time = null; this.startPic = this.bodybg.css('backgroundImage'); this.dragBtn = false; this.playing = false; }, _bindElement : function(){ var that = this; var that = this; this.turn.click(function(){ that.trunNum++; var status = that.trunNum % 3; switch(status){ case 0: that.turn.html('順'); break; case 1: that.randomArr.sort(function(){ return Math.random()>0.5?-1:1; }); that.turn.html('隨'); break; case 2: that.turn.html('單'); break; } }); //下一首歌 this.music_before.click(function(){ that._beforeMusic(); }); //上一首歌 this.music_after.click(function(){ that._afterMusic() }); this.music_switch.on('click',function(){ that._fnPlay(); }); this.lineW = this.process_line.width() - this.dot.width(); function _touchstart( event ){ that.dragBtn = true; that.maxX = that.process_line.width() - that.dot.width(); event.stopPropagation(); } function _touchmove( event ){ var touch = event.targetTouches[0]; var moverX = touch.pageX; that.dragBtn = true; moverX = moverX < 0 ? 0 : moverX; moverX = moverX > that.maxX ? that.maxX : moverX; that.dot.css('left',moverX); that.line_after.css('width',moverX); that.dragX = touch.pageX; event.stopPropagation(); } function _touchend( event ){ var touch = event.targetTouches[0]; event.stopPropagation(); _tapLine(); } this.process_line.click(function(event){ var eX = event.clientX > that.lineW ? that.lineW : event.clientX; that.dot.css('left',eX); that.line_after.css('width',eX); _tapLine(); }); function _tapLine(){ var l = parseFloat(that.dot.css('left')); var maxL = parseFloat(that.lineW); that._audio.currentTime = that._percentage( l,maxL,that._audio.duration); clearInterval(that.Time); that._musicTimer(); that.dragBtn = false; } this._dot.addEventListener('touchstart',_touchstart,false); this._dot.addEventListener('touchmove',_touchmove,false); this._dot.addEventListener('touchend',_touchend,false); }, createAudio : function(){ var html = '<audio id="audio" >您的瀏覽器不支持音頻元素。</audio>'; $('body').append(html); this._audio = document.getElementById('audio'); }, createMusicList : function(){ var that = this; var html = ''; var data = this.data; this._getHeight(); for( var i=0,len=data.length;i<len;i++ ){ var name,singer,src,pic; if( toStrings(data[i].name).length > 0){ name = toStrings(data[i].name); }else{ name = '未知'; } if( toStrings(data[i].singer).length > 0){ singer = toStrings(data[i].singer); }else{ singer = '未知'; } /* if( data[i].cover ){ pic = data[i].cover; }else{ pic = this.startPic; } */ pic = data[i].cover; src = data[i].src; html += '<li music_index='+i+' music_name='+ name +' music_singer='+ singer +' music_src='+src+' music_pic='+ pic +'>' +'<div class="music_mian">' + '<p class="music_name">'+name+'</p>' + '<p class="music_singer">'+singer+'</p>' + '</div>' + '<div class="list_line"></div>' +'</li>'; } this.list_ul.html(html); //列表綁定事件; this.list_ul.on('click','li',function(){ that._bindElement();//建立事件; that._cutSongs(this); }); //轉換字符去掉空格; function toStrings( txt ){ return txt.split(' ').join(' ').toString(); } }, _afterMusic : function(){ var status = this.trunNum % 3; var current = this.list_ul.find('.cur'); var index = current.index(); var maxLen = this.data.length-1; var obj; if( status == 0 || status == 2 ){ if( index == 0 ){ obj = this.list_ul.find('li').last(); }else if( index > 0 ){ obj = current.prev(); } }else if( status == 1 ){ for(var i=0;i<maxLen;i++){ if( this.randomArr[i] == index ){ var num = i; num--; if( num <= 0 ){ num = maxLen; } obj = this.list_ul.find('li').eq(this.randomArr[num]); } } } this._cutSongs(obj); }, _beforeMusic : function(){ var status = this.trunNum % 3; var current = this.list_ul.find('.cur'); var index = current.index(); var maxLen = this.data.length-1; var obj; if( status == 0 || status == 2 ){ if( index == maxLen ){ obj = this.list_ul.find('li').first(); }else if( index < maxLen ){ obj = current.next(); } }else if( status == 1 ){ for(var i=0;i<maxLen;i++){ if( this.randomArr[i] == index ){ var num = i; num++; if( num >= maxLen ){ num = 0 } obj = this.list_ul.find('li').eq(this.randomArr[num]); } } } this._cutSongs(obj); }, _cutSongs : function( obj ){ this.playing = true; $(obj).addClass('cur').siblings('li').removeClass('cur'); var music_src = $(obj).attr('music_src'); var music_pic = $(obj).attr('music_pic'); var music_name = $(obj).attr('music_name'); var music_singer = $(obj).attr('music_singer'); $('#name_txt').html(music_name); $('#singer_txt').html(music_singer); if( music_pic ){ this.bodybg.attr('style','background-image:url('+music_pic+');' ); }else{ this.bodybg.css('backgroundImage',this.startPic+';'); } this.bodybg.addClass('cur'); this._audio.src = music_src; this._fnPlay(); }, _getHeight :function(){ var wH = $(window).height(); var mH = $('#audio_main').height(); var $musicMain = $('#list_main'); var h = wH - mH; $musicMain.height(h); }, _fnPlay : function(){ var that = this; if( this._audio.paused ){ //判斷是否從新加載歌曲 if(this.playing){ this._audio.load(); this.playing = false; } this._audio.play(); this.music_switch.addClass('cur'); this._audio.addEventListener('loadeddata',function(){ var timerMain = that._toTime(that._audio.duration); var len = timerMain.split(':').length; switch(len){ case 1: that.time_start.html('00'); break; case 2: that.time_start.html('00:00'); break; } that.time_end.html(timerMain); that._musicTimer(); },false); }else{ this._audio.pause(); this.music_switch.removeClass('cur'); clearInterval(that.Time); } }, _percentage:function( a,b,c ){ return a/b*c; }, _musicTimer : function(){ var that = this; this.Time = setInterval(_snowTime,1000); var timerMain = that._toTime(that._audio.duration); var len = timerMain.split(':').length; function _snowTime(){ if( that._audio.currentTime == that._audio.duration ){ clearInterval(that.Time); var status = that.trunNum % 3; switch(status){ case 0: that._beforeMusic(); break; case 1: that._beforeMusic(); break; case 2: var current = that.list_ul.find('.cur'); that._cutSongs(current); break; } } if(!that.dragBtn){ var timerMain = that._toTime(that._audio.currentTime); var l = that._percentage( that._audio.currentTime,that._audio.duration,parseFloat(that.lineW) ); that.dot.css('left',l); that.line_after.css('width',l); } switch( len ){ case 1: that.time_start.html(timerMain); break; case 2: var timer = timerMain.indexOf(':')>0?timerMain:'00:'+timerMain; that.time_start.html(timer); break; } } }, _toTime : function( time ){ var hour = Math.floor(time/3600); var min = Math.floor(time%3600/60); var sec = Math.floor(time%60); var ih = hour <= 9 ? '0'+hour : hour; var im = min <= 9 ? '0'+min :min; var is = sec <= 9 ? '0'+sec :sec; return ( ih > 0 ? ih+':' : '') + (im >0 ? im+':' :'') +is; } }
總結:
最開始想用 面向對象的方式寫,可是後來仍是以爲json單體的寫法更實用
我的簡單粗暴的判斷方法是:
頁面 單一功能的應用 就用 json單體;
頁面 某功能屢次調用 就用 面向對象 好比: tab選項卡、輪播之類的;
hml5 audio 的新的api 文上的鏈接講比較詳盡就不贅述了
遇到的我的一些小問題:
一:第一次用這個 zepto.js 感受跟 jQuery 同樣,不過這個 tap 的事件怎麼綁定不上 後來仍是用 click 代替了 以前據說在 click 在移動端有延遲 也由於是用了 zepto 庫還沒綁定上徹底用的跟 JQuery 似的也讓我很尷尬,同時也由於用了庫在 dom 的獲取也用了原生因此在這個dom存儲上有所難以區分(固然我知道這對象能夠轉成原生,最後仍是用了純原生的方式獲取),由於 audio 有不少新的屬性 都須要原生;
二:單體的this 指向 須要不停的 存成 that在用 有點繁瑣;
三:歌曲時間想作到小時以上 好比 : 01:02:05 這樣 後來 在這個當前播放時間設置 _snowTime() 裏 判斷一句就是 _toTime()返回 當前播放秒數轉成分鐘形式的字符串 (如:00:00:00)裏的":"的個數,在時間補零(00:00:05)上 還要在用這個 ':'作判斷以爲噁心,後來看了酷狗的播放器上太小時的也是用分鐘的形式展現(120:30)目前還沒想到什麼好一點解決方法,也能夠說是目前的一個已知 bug 吧;
四:最初的功能方法上想的比較獨立單一,起初覺得 「上一首歌」,「下一首歌」,和「歌曲播放到最後的下一首歌」 都是 獨立的 其「歌曲播放到最後的下一首歌」其實之分兩種一種是單曲循環,第二種是「下一首歌」 因此 「歌曲播放到最後的下一首歌」 區分不是 「單曲循環」 模式 剩下都是 「下一首歌」 裏的功能 在「下一首歌」裏作這個 歌曲模式「隨機」、「順序」播放的下一首就但是了;
五:在作這個「歌曲進度」拖動的時候,和這個歌曲正常播放 設置 「歌曲進度」 的衝突,後來創建that.dragBtn 判斷是拖動仍是僅僅是歌曲進度的設置,一樣的問題還有這個 當前歌曲的暫停以後的播放和切割以後的播放 是有所不一樣的,歌曲的切換時是換了audio的路徑 須要audio.load() 重新加載才能播放一樣建立了this.playing 來判斷是否切歌;
六:歌曲模式的設置 最開始想多了 想創建一個 json {‘順’,‘隨’,‘單’} 作對應關係,後來發現 其實只須要一個 num 作累加取餘去判斷便可;
功能總結:順序播放、隨機播放、單曲循環、上下換歌、點擊切歌、拖動歌曲的進度;
沒作功能:沒有音控、手機轉屏重新設置....沒怎麼作適配,僅僅適配本身的手機,確定還有Bug和代碼如何在優化歡迎指出;
謝謝 大牛們 寫的技術文章作過的demo的共享~