久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合

站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例

技術(shù)點(diǎn):ES6+Webpack+HTML5 Audio+Sass

這里,我們將一步步的學(xué)到如何從零去實(shí)現(xiàn)一個(gè)H5音樂播放器。

首先來看一下最終的實(shí)現(xiàn)效果:Demo鏈接

接下來就步入正題:

  1. 要做一個(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對(duì)象,包含已緩沖的時(shí)間段信息,即加載進(jìn)度。該對(duì)象包含一個(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ā)的事件流以及我們?cè)诓煌瑫r(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等等;

  2. 確定整體結(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ù)器。

  3. 確定播放器UI和交互:
    可能關(guān)于界面每個(gè)人有自己的想法,這里就不過多贅述了,以我做好的播放器UI為例進(jìn)行分解
    實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例
    從界面中可以看出一個(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í)例

  4. 確立項(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í)間格式等等:

    實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(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);      }  };

    View Code

    由于設(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)部:

    實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例

    class skPlayer {      constructor(option){      }        template(){      }        init(){      }        bind(){      }        prev(){      }        next(){      }        switchMusic(index){      }        play(){      }        pause(){      }        toggle(){      }        toggleList(){      }        toggleMute(){      }        switchMode(){      }        destroy(){      }  }

    View Code

    實(shí)例判斷,如果存在返回?zé)o原型的空對(duì)象,因?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();
    實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例

        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';      }

    View Code

    事件綁定,主要綁定audio的事件以及操作面板的事件:

                this.bind();
    實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例實(shí)現(xiàn)一個(gè)HTML5音樂播放器的實(shí)例

        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();                  }              });          }      }

    View Code

    至此,核心代碼基本完成,接下來就是自己根據(jù)需要完成API部分。
    最后我們暴露模塊:

    module.exports = skPlayer;

    一個(gè)HTML5音樂播放器就大功告成了 ~ !

贊(0)
分享到: 更多 (0)
?
網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合
青青草精品视频| 亚洲精品.com| 免费人成在线不卡| 欧美日韩国产亚洲一区| 欧美成人高清| 久久久久国产精品一区二区| 999国产精品视频| 欧美综合另类| 亚洲综合精品| 亚洲精品系列| 国产精品网在线观看| 久久精品三级| 蜜桃av在线播放| 激情久久中文字幕| 黄色av一区| 日韩在线观看中文字幕| 欧美日韩精品一区二区三区视频 | 午夜欧美精品| 影音先锋久久| 亚洲一二三区视频| 国产欧美啪啪| sm久久捆绑调教精品一区| 久久激情中文| 久久福利影视| 国产日产精品一区二区三区四区的观看方式 | 久久不射网站| 欧美精品三级在线| 国产精品多人| 日韩亚洲一区在线| 亚洲在线国产日韩欧美| 日韩高清欧美激情| 岛国精品一区| 亚洲国产不卡| 日韩和欧美的一区| 久久一区亚洲| 久久中文字幕二区| 亚洲a级精品| 国产成人久久| 久久性天堂网| 国产精品亚洲欧美一级在线| 日韩精品欧美| 日韩中出av| 精品久久网站| 欧美日韩国产在线一区| 国产日韩一区二区三免费高清| 伊伊综合在线| 巨乳诱惑日韩免费av| 国产精品中文字幕制服诱惑| 色婷婷精品视频| 日本aⅴ精品一区二区三区| 日产精品一区二区| 久久亚洲风情| 91亚洲国产| 蜜臀av亚洲一区中文字幕| 国产福利资源一区| 黄色在线一区| 国产一区二区色噜噜| 国产手机视频一区二区| 久久av影视| 日韩影院在线观看| 97精品国产福利一区二区三区| 亚洲一区二区小说| 日韩欧美一区二区三区在线视频 | 久久亚洲精品伦理| 日本一二区不卡| 亚洲深深色噜噜狠狠爱网站| 岛国av免费在线观看| 亚洲精选av| 久久中文字幕av一区二区不卡| 国产亚洲一区| 国产精品婷婷| 吉吉日韩欧美| 国产伦精品一区二区三区在线播放| 激情婷婷亚洲| 精品视频91| 欧美天堂在线| 另类av一区二区| 六月婷婷综合| 国产精品va视频| 日韩精品国产精品| 亚洲激情五月| 98精品视频| 国产精品v一区二区三区| 免费欧美在线视频| 亚洲va在线| 精品三级av在线导航| 天堂va在线高清一区| 亚洲激精日韩激精欧美精品| sm久久捆绑调教精品一区| 国产精品啊v在线| 日本亚洲最大的色成网站www | 精品一区二区三区中文字幕视频 | 视频一区免费在线观看| 亚洲成人精品| 久久久久久婷| 国产亚洲一卡2卡3卡4卡新区| 性色av一区二区怡红| 欧美日韩视频免费观看| 成人在线视频免费看| 日韩精品一页| 亚洲一级大片| 另类激情亚洲| 99热精品在线观看| 久久精品91| 麻豆精品蜜桃| 日韩精品免费一区二区在线观看| 黄色在线网站噜噜噜| 风间由美中文字幕在线看视频国产欧美| 日韩成人午夜精品| 亚洲精品自拍| 亚洲综合激情在线| 丝袜美腿亚洲一区二区图片| 99精品国产一区二区三区| 精品久久久中文字幕| 国产亚洲精品精品国产亚洲综合| 日本不卡一区二区| 日本精品久久| 欧美一级二区| 欧美一区二区三区久久| 日韩激情精品| 国产日韩欧美一区二区三区在线观看 | 午夜在线视频一区二区区别| 午夜欧美精品| 国产精品.xx视频.xxtv| 成人午夜在线| 国产无遮挡裸体免费久久| 欧美日韩精品在线一区| 国产一区二区三区网| 国产毛片精品| 日本综合视频| 亚洲人成网77777色在线播放 | 亚洲人成在线影院| 国产欧美日韩影院| 麻豆久久一区| 久久天堂成人| 日本不卡一区二区三区| 18国产精品| 日韩一区二区在线免费| 99国产一区| 国产精品亚洲成在人线| 成人福利av| 日精品一区二区三区| av资源中文在线天堂| 亚洲免费激情| 国产精品2023| аⅴ资源天堂资源库在线| caoporn视频在线| 亚洲二区免费| 日韩精品午夜| 日韩精品一卡二卡三卡四卡无卡| 国产亚洲在线观看| 99久久夜色精品国产亚洲狼| 日韩亚洲精品在线| 日本中文字幕不卡| 国产精品成久久久久| 激情综合网五月| 日韩高清中文字幕一区| 成人三级高清视频在线看| 久热综合在线亚洲精品| 国产免费久久| 国产在线不卡| 69堂精品视频在线播放| 日本不卡免费高清视频在线| 伊人精品视频| 国产精品午夜一区二区三区| 欧洲一级精品| 亚洲精品观看| 91日韩在线| 日韩三级一区| 亚洲性视频h| 美女视频一区在线观看| 日韩视频中文| 蜜臀久久精品| 国产精品地址| 日本一区二区三区视频在线看| 91精品国产乱码久久久久久久| 欧美久久精品| 丝袜国产日韩另类美女| 亚洲国产一区二区三区在线播放| 欧美sss在线视频| 99久久亚洲精品蜜臀| 亚洲啊v在线免费视频| 国产在线欧美| 亚洲一区资源| 欧美日韩精品一区二区三区视频 | 国产精品av久久久久久麻豆网| av亚洲一区二区三区| 色一区二区三区四区| 91亚洲无吗| 一区二区三区国产盗摄| 在线日韩欧美| 在线人成日本视频| 亚洲涩涩在线| 中文一区二区| 久久精品99国产精品| 麻豆精品视频在线观看| 综合日韩av| 美女久久一区| 久久97视频| 亚洲欧美日本视频在线观看|