這篇文章主要介紹了HTML5如何實(shí)現(xiàn)音樂播放器,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)公司長期為1000多家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為成華企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站,成華網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
要做一個(gè)音樂播放器就要非常了解在Web中音頻播放的方式,通常都采用HTML5的audio標(biāo)簽
關(guān)于audio標(biāo)簽,它有大量的屬性、方法和事件,在這里我就做一個(gè)大致的介紹。
屬性:
src:必需,音頻來源;
controls:常見,設(shè)置后顯示瀏覽器默認(rèn)的audio控制面板,不設(shè)置默認(rèn)隱藏audio標(biāo)簽;
autoplay:常見,設(shè)置后自動(dòng)播放音頻(移動(dòng)端不支持);
loop:常見,設(shè)置后音頻將循環(huán)播放;
preload:常見,設(shè)置音頻預(yù)加載(移動(dòng)端不支持);
volume:少見,設(shè)置或返回音頻大小,值為0-1之間的一個(gè)浮點(diǎn)數(shù)(移動(dòng)端不支持);
muted:少見,設(shè)置或返回靜音狀態(tài);
duration:少見,返回音頻時(shí)長;
currentTime:少見,設(shè)置或返回當(dāng)前播放時(shí)間;
paused:少見,返回當(dāng)前播放狀態(tài),是否暫停;
buffered:少見,一個(gè)TimeRanges對象,包含已緩沖的時(shí)間段信息,即加載進(jìn)度。該對象包含一個(gè)屬性length,返回一個(gè)從0開始的數(shù)表示當(dāng)前緩沖了多少段音頻;還包含兩個(gè)方法,start、end,分別需要傳入一個(gè)參數(shù),即傳入音頻已加載的第幾段,從0開始。start返回該段的起始時(shí)間,end返回該段的終點(diǎn)時(shí)間。舉例:即傳入0,第一段的起始是0,終止時(shí)間是17,單位秒;
屬性就介紹到這里,可能還有一些比較少用的屬性如:playbackRate等,在視頻播放中可能會(huì)用到,我就暫不講解。
方法:
play():開始播放音頻;
pause():暫停播放音頻;
事件:
canplay:當(dāng)前音頻可以開始播放(只加載了部分buffered,并未全部加載完成);
canplaythrough:可以無停頓播放(即音頻全部加載完成);
durationchange:音頻時(shí)長發(fā)生變化;
ended:播放結(jié)束;
error:發(fā)生錯(cuò)誤;
pause:播放暫停;
play:播放開始;
progress:音頻下載過程中觸發(fā),事件觸發(fā)過程中可以通過訪問audio的buffered屬性獲取加載進(jìn)度;
seeking:音頻跳躍中觸發(fā),即為修改currentTime時(shí);
seeked:音頻跳躍完成時(shí)觸發(fā),即為修改完成currentTime時(shí);
timeupdate:音頻播放過程中觸發(fā),同時(shí)currentTime屬性在同步更新;
事件就介紹到這里,可能還有一些不常用的事件暫不講解。
最后再講解一下一個(gè)音頻從開始加載到播放結(jié)束過程中,所觸發(fā)的事件流以及我們在不同時(shí)間段可以操作的屬性:
loadstart:開始加載;
durationchange:獲取到音頻時(shí)長(此時(shí)可以獲取duration屬性);
progress:音頻下載中(將伴隨下載過程一直觸發(fā),此時(shí)可以獲取buffered屬性);
canplay:所加載的音頻足夠開始播放(每次暫停后開始播放也會(huì)觸發(fā));
canplaythrough:音頻全部加載完成;
timeupdate:播放過程中(currentTime屬性伴隨著同步更新);
seeking:修改當(dāng)前播放進(jìn)度中(即為修改currentTime屬性);
seeked:修改當(dāng)前播放進(jìn)度完成;
ended:播放完成;
這就是整個(gè)音頻的大致事件流,可能有一些少用的事件沒有列舉出。
在事件觸發(fā)過程中,有一些屬性在音頻還沒有開始加載的時(shí)候就可以設(shè)置,如:controls、loop、volume等等;
確定整體結(jié)構(gòu):
因?yàn)樽约菏亲龀刹寮姆绞桨l(fā)布在npm上供他人使用的,所以我們就采用面向?qū)ο蟮姆绞竭M(jìn)行代碼編寫,又因?yàn)橛脩舻男枨蟛灰唬栽谠O(shè)計(jì)之初就暴露出大量的API和配置項(xiàng)以滿足大部分用戶的需求。
這里因?yàn)樽约焊?xí)慣es6的語法,就全程以es6為基礎(chǔ)進(jìn)行開發(fā),同時(shí)為了開發(fā)效率,又使用了sass進(jìn)行css的編寫,最后還使用了webpack和webpack-dev-server用以編譯es6和sass,項(xiàng)目打包,構(gòu)建本地服務(wù)器。
確定播放器UI和交互:
可能關(guān)于界面每個(gè)人有自己的想法,這里就不過多贅述了,以我做好的播放器UI為例進(jìn)行分解
從界面中可以看出一個(gè)播放器所需要的最基礎(chǔ)功能:
播放/暫停、封面/歌名/歌手的顯示、播放進(jìn)度條/加載進(jìn)度條/進(jìn)度操作功能、循環(huán)模式切換、進(jìn)度文字更新/歌曲時(shí)長、靜音/音量大小控制、列表顯示狀態(tài)控制、點(diǎn)擊列表項(xiàng)切歌功能
再結(jié)合我們想要滿足用戶需求,提供配置項(xiàng)和API的出發(fā)點(diǎn)可以得出我們想設(shè)計(jì)的配置項(xiàng)和暴露的API項(xiàng):
配置項(xiàng):自動(dòng)播放是否開啟、默認(rèn)歌曲列表的顯示狀態(tài)、默認(rèn)循環(huán)模式的設(shè)置
API:播放/暫停/toggle、循環(huán)模式的切換、靜音/恢復(fù)、列表顯示狀態(tài)的切換、上一曲/下一曲/切歌、銷毀當(dāng)前實(shí)例
確立項(xiàng)目結(jié)構(gòu),開始編碼:
因?yàn)槭褂脀ebpack,所以我們直接將css打包至js內(nèi),以便作為插件供用戶使用:
require('./skPlayer.scss');
抽離公共方法,在播放器中有很多可能需要抽離的公共方法如:點(diǎn)擊播放進(jìn)度條和音量進(jìn)度條時(shí)需要計(jì)算鼠標(biāo)距離進(jìn)度條左端的距離以進(jìn)行進(jìn)度跳轉(zhuǎn),時(shí)間從duratin中獲取到的以秒為單位的時(shí)間轉(zhuǎn)換成標(biāo)準(zhǔn)時(shí)間格式等等:
const Util = { leftDistance: (el) => { let left = el.offsetLeft; let scrollLeft;while (el.offsetParent) { el = el.offsetParent; left += el.offsetLeft; } scrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;return left - scrollLeft; }, timeFormat: (time) => { let tempMin = parseInt(time / 60); let tempSec = parseInt(time % 60); let curMin = tempMin < 10 ? ('0' + tempMin) : tempMin; let curSec = tempSec < 10 ? ('0' + tempSec) : tempSec;return curMin + ':' + curSec; }, percentFormat: (percent) => {return (percent * 100).toFixed(2) + '%'; }, ajax: (option) => { option.beforeSend && option.beforeSend(); let xhr = new XMLHttpRequest(); xhr.onreadystatechange = () => {if(xhr.readyState === 4){if(xhr.status >= 200 && xhr.status < 300){ option.success && option.success(xhr.responseText); }else{ option.fail && option.fail(xhr.status); } } }; xhr.open('GET',option.url); xhr.send(null); } };
由于設(shè)計(jì)之初,考慮到播放器的獨(dú)特性,設(shè)計(jì)為只能存在一個(gè)實(shí)例,設(shè)置了一個(gè)全局變量以判斷當(dāng)前是否存在實(shí)例:
let instance = false;
在使用ES6的情況下,我們將主邏輯放在構(gòu)造函數(shù)內(nèi)部,將通用性強(qiáng)和API放在公共函數(shù)內(nèi)部:
class skPlayer { constructor(option){ } template(){ } init(){ } bind(){ } prev(){ } next(){ } switchMusic(index){ } play(){ } pause(){ } toggle(){ } toggleList(){ } toggleMute(){ } switchMode(){ } destroy(){ } }
實(shí)例判斷,如果存在返回?zé)o原型的空對象,因?yàn)镋S6構(gòu)造函數(shù)內(nèi)默認(rèn)返回帶原型的實(shí)例:
if(instance){ console.error('SKPlayer只能存在一個(gè)實(shí)例!');return Object.create(null); }else{ instance = true; }
初始化配置項(xiàng),默認(rèn)配置與用戶配置合并:
const defaultOption = { ... };this.option = Object.assign({},defaultOption,option);
將常用屬性綁定在實(shí)例上:
this.root = this.option.element;this.type = this.option.music.type;this.music = this.option.music.source;this.isMobile = /mobile/i.test(window.navigator.userAgent);
一些公共的API內(nèi)部this指向在默認(rèn)情況下指向?qū)嵗菫榱藴p少代碼量,將操作界面上的功能與API調(diào)用一套代碼,在綁定事件的時(shí)候this指向會(huì)改變,所以通過bind的方式綁定this,當(dāng)然也可以在綁定事件的時(shí)候使用箭頭函數(shù):
this.toggle = this.toggle.bind(this);this.toggleList = this.toggleList.bind(this);this.toggleMute = this.toggleMute.bind(this);this.switchMode = this.switchMode.bind(this);
接下來,我們就使用ES6字符串模板開始生成HTML,插入到頁面中:
this.root.innerHTML = this.template();
接下來初始化,初始化過程中將常用DOM節(jié)點(diǎn)綁定,初始化配置項(xiàng),初始化操作界面:
this.init();
init(){this.dom = { cover: this.root.querySelector('.skPlayer-cover'), playbutton: this.root.querySelector('.skPlayer-play-btn'), name: this.root.querySelector('.skPlayer-name'), author: this.root.querySelector('.skPlayer-author'), timeline_total: this.root.querySelector('.skPlayer-percent'), timeline_loaded: this.root.querySelector('.skPlayer-line-loading'), timeline_played: this.root.querySelector('.skPlayer-percent .skPlayer-line'), timetext_total: this.root.querySelector('.skPlayer-total'), timetext_played: this.root.querySelector('.skPlayer-cur'), volumebutton: this.root.querySelector('.skPlayer-icon'), volumeline_total: this.root.querySelector('.skPlayer-volume .skPlayer-percent'), volumeline_value: this.root.querySelector('.skPlayer-volume .skPlayer-line'), switchbutton: this.root.querySelector('.skPlayer-list-switch'), modebutton: this.root.querySelector('.skPlayer-mode'), musiclist: this.root.querySelector('.skPlayer-list'), musicitem: this.root.querySelectorAll('.skPlayer-list li') };this.audio = this.root.querySelector('.skPlayer-source');if(this.option.listshow){this.root.className = 'skPlayer-list-on'; }if(this.option.mode === 'singleloop'){this.audio.loop = true; }this.dom.musicitem[0].className = 'skPlayer-curMusic'; }
事件綁定,主要綁定audio的事件以及操作面板的事件:
this.bind();
bind(){this.updateLine = () => { let percent = this.audio.buffered.length ? (this.audio.buffered.end(this.audio.buffered.length - 1) / this.audio.duration) : 0;this.dom.timeline_loaded.style.width = Util.percentFormat(percent); };// this.audio.addEventListener('load', (e) => {// if(this.option.autoplay && this.isMobile){// this.play();// }// });this.audio.addEventListener('durationchange', (e) => {this.dom.timetext_total.innerHTML = Util.timeFormat(this.audio.duration);this.updateLine(); });this.audio.addEventListener('progress', (e) => {this.updateLine(); });this.audio.addEventListener('canplay', (e) => {if(this.option.autoplay && !this.isMobile){this.play(); } });this.audio.addEventListener('timeupdate', (e) => { let percent = this.audio.currentTime / this.audio.duration;this.dom.timeline_played.style.width = Util.percentFormat(percent);this.dom.timetext_played.innerHTML = Util.timeFormat(this.audio.currentTime); });//this.audio.addEventListener('seeked', (e) => {// this.play();//});this.audio.addEventListener('ended', (e) => {this.next(); });this.dom.playbutton.addEventListener('click', this.toggle);this.dom.switchbutton.addEventListener('click', this.toggleList);if(!this.isMobile){this.dom.volumebutton.addEventListener('click', this.toggleMute); }this.dom.modebutton.addEventListener('click', this.switchMode);this.dom.musiclist.addEventListener('click', (e) => { let target,index,curIndex;if(e.target.tagName.toUpperCase() === 'LI'){ target = e.target; }else{ target = e.target.parentElement; } index = parseInt(target.getAttribute('data-index')); curIndex = parseInt(this.dom.musiclist.querySelector('.skPlayer-curMusic').getAttribute('data-index'));if(index === curIndex){this.play(); }else{this.switchMusic(index + 1); } });this.dom.timeline_total.addEventListener('click', (event) => { let e = event || window.event; let percent = (e.clientX - Util.leftDistance(this.dom.timeline_total)) / this.dom.timeline_total.clientWidth;if(!isNaN(this.audio.duration)){this.dom.timeline_played.style.width = Util.percentFormat(percent);this.dom.timetext_played.innerHTML = Util.timeFormat(percent * this.audio.duration);this.audio.currentTime = percent * this.audio.duration; } });if(!this.isMobile){this.dom.volumeline_total.addEventListener('click', (event) => { let e = event || window.event; let percent = (e.clientX - Util.leftDistance(this.dom.volumeline_total)) / this.dom.volumeline_total.clientWidth;this.dom.volumeline_value.style.width = Util.percentFormat(percent);this.audio.volume = percent;if(this.audio.muted){this.toggleMute(); } }); } }
至此,核心代碼基本完成,接下來就是自己根據(jù)需要完成API部分。
最后我們暴露模塊:
module.exports = skPlayer;
一個(gè)HTML5音樂播放器就大功告成了 ~ !
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享HTML5如何實(shí)現(xiàn)音樂播放器內(nèi)容對大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問題就找創(chuàng)新互聯(lián),詳細(xì)的解決方法等著你來學(xué)習(xí)!
名稱欄目:HTML5如何實(shí)現(xiàn)音樂播放器
文章網(wǎng)址:http://m.newbst.com/article0/gdidio.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、、網(wǎng)站設(shè)計(jì)公司、微信公眾號(hào)、搜索引擎優(yōu)化、移動(dòng)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)