摘要:簡(jiǎn)介是一款簡(jiǎn)潔漂亮的音樂(lè)播放器在我第一次看到這款播放器顏值的時(shí)候讓我眼前一亮,我非常崇拜那些能設(shè)計(jì)出好看界面的設(shè)計(jì)師但是在用過(guò)之后發(fā)現(xiàn)還是有不足的地方這是我曾經(jīng)提過(guò)的用了一段時(shí)間,很喜歡簡(jiǎn)潔的,提一些其他可改進(jìn)的建議我認(rèn)為有必要提供動(dòng)態(tài)管理
簡(jiǎn)介
@DIYgod/APlayer 是一款簡(jiǎn)潔漂亮的 HTML5 音樂(lè)播放器 (〃?ω?)
在我第一次看到這款播放器顏值的時(shí)候讓我眼前一亮,我非常崇拜那些能設(shè)計(jì)出好看界面的設(shè)計(jì)師 (* >ω<)
但是在用過(guò)之后發(fā)現(xiàn)還是有不足的地方 這是我曾經(jīng)提過(guò)的 Issues
用了一段時(shí)間,很喜歡 APlayer 簡(jiǎn)潔的 UI,提一些其他可改進(jìn)的建議:
1.我認(rèn)為有必要提供動(dòng)態(tài)管理播放列表的 API
(如果沒(méi)有,在需要?jiǎng)討B(tài)添加歌曲到列表時(shí)只能重新初始化)
2.應(yīng)該提供一個(gè)銷毀播放器的 API
3.歌詞允許異步添加,通常獲取歌詞接口是多帶帶的
(現(xiàn)在必須等待歌詞接口返回再初始化播放器,若歌詞獲取失敗或時(shí)間過(guò)長(zhǎng)會(huì)同時(shí)影響到播放音樂(lè)功能)
關(guān)于第三條,APlayer 其實(shí)是支持異步歌詞的但僅支持傳入 .lrc 文件的地址
如果是像網(wǎng)易云/QQ音樂(lè)那樣返回的是 JSON 格式的那就不滿足需求了
為什么不提 PR 要重寫(xiě)呢?
這個(gè)我想了一會(huì),最終還是覺(jué)得組件化的方式開(kāi)發(fā)更好一些(原 APlayer 用的是原生 JS 沒(méi)有依賴別的庫(kù))
而且因?yàn)槲乙郧斑€在做后端的時(shí)候就自己寫(xiě)過(guò)音樂(lè)播放器(仿微博播放器,當(dāng)時(shí)不會(huì)用 Git 源碼已丟)
所以挺有經(jīng)驗(yàn)的,重寫(xiě)一個(gè)難度也不大,而且比較隨心所欲,還可以隨意加一些自己想要的東西 qwq
說(shuō)明:該播放器是基于 @DIYgod/APlayer 的布局和 樣式 采用 TS + Vue 組件化重構(gòu)的
演示:http://aplayer.quq.cat
文檔:http://aplayer.quq.cat/docs
源碼:https://github.com/MoeFE/vue-...
NPM:https://www.npmjs.com/package...
播放列表來(lái)自網(wǎng)易云歌單:http://music.163.com/#/playli...
如果喜歡的話別忘了點(diǎn)一個(gè) star 喲 (*ゝω?)
歡迎提 Issues 和 PR (′?_?`)
為了大家使用方便,我選擇 Vue ,可以響應(yīng)式控制播放器各個(gè)屬性 并以插件的形式發(fā)布(詳情請(qǐng)看 demo )
我這里為了方便大家能更好的調(diào)試,在生產(chǎn)環(huán)境下開(kāi)啟了 SourceMap 和 devtools
如果您安裝了 vue-devtools 可以打開(kāi)調(diào)試器查看組件劃分和各個(gè)組件的信息
至于為什么選擇用 TypeScript 本文就不做過(guò)多的解釋了
大家可以自行在網(wǎng)上找一下 TypeScript 和 JavaScript 的區(qū)別
我只能告訴你:對(duì)于一個(gè)曾經(jīng)使用 C# 的開(kāi)發(fā)者來(lái)說(shuō)這簡(jiǎn)直不能太爽啦 微軟爸爸賽高 (* >ω<)
最后推薦一款 TS + Vue 的腳手架模版:https://github.com/Toilal/vue...
以后或?qū)⒓尤氲焦俜侥0嬷校篽ttps://github.com/vuejs-temp...
TS + React 的腳手架可以用這個(gè):https://github.com/wmonk/crea...
拆分組件拿到布局樣式后要做的第一件事情就是拆分組件
將 @DIYgod/APlayer 的布局和 樣式 復(fù)制過(guò)來(lái)
確保樣式?jīng)]有問(wèn)題后再將各個(gè)組件的布局和樣式多帶帶復(fù)制出來(lái)
不懂設(shè)計(jì)的只好復(fù)制了 請(qǐng)?jiān)试S我做一個(gè)悲傷的表情 (? ? ? )
我將播放器拆分成了以下組件:
組件名稱 | 組件說(shuō)明 |
---|---|
APlayer.ts | 播放器容器組件 |
Button.ts | 按鈕組件 |
Picture.ts | 歌曲圖片組件 |
Container.ts | 右側(cè)容器組件 |
Info.ts | 歌曲信息組件 |
Lyric.ts | 歌詞面板組件 |
Progress.ts | 進(jìn)度條組件 |
Time.ts | 播放時(shí)間組件 |
Volume.ts | 音量控制組件 |
List.ts | 播放列表組件 |
Item.ts | 播放列表項(xiàng)組件 |
再來(lái)一張更清晰的圖片吧:
點(diǎn)擊查看高清原圖
功能開(kāi)發(fā)功能開(kāi)發(fā)其實(shí)沒(méi)有多少難度,HTML5 已經(jīng)封裝好了 HTMLAudioElement 元素
我們就是用一下它的 API 和視圖進(jìn)行數(shù)據(jù)綁定和交互而已 看一下文檔就好了
不過(guò)這里會(huì)遇到一個(gè)小問(wèn)題,那就是 Vue 不能監(jiān)聽(tīng)到 Audio 對(duì)象的屬性變化
因?yàn)?Audio 對(duì)象其實(shí)就是 HTMLAudioElement 元素,Vue 是不能監(jiān)聽(tīng)到元素屬性變化的,所以我想了一個(gè)小辦法
定義了一個(gè) Media 接口,里面定義了和 Audio 對(duì)象相同的屬性,在 Audio 的事件中對(duì) Media 的屬性進(jìn)行同步
這樣的話,就可以使用 Media 對(duì)象響應(yīng)式獲取 Audio 的屬性值
可以查看這一段代碼:APlayer.ts#L326-L334
我這里簡(jiǎn)單介紹一些比較常用的屬性和方法吧
名稱 | 說(shuō)明 |
---|---|
autoplay | 是否自動(dòng)播放 (在 Safari 中無(wú)效,可以自行在初始化音頻后手動(dòng)調(diào)用 play 方法) |
bufferd | 獲取已緩沖的進(jìn)度(必須在 readeyState >= 3 之后獲取,否則會(huì)拋異常) |
loop | 是否循環(huán)播放音頻(推薦根據(jù)當(dāng)前播放模式自行實(shí)現(xiàn)該功能) |
preload | 預(yù)加載選項(xiàng),推薦使用 metadata,在未播放時(shí)僅獲取音頻的長(zhǎng)度,而不要加載整個(gè)音頻 |
src | 獲取或設(shè)置音頻的播放地址 |
volume | 獲取或設(shè)置音頻的音量(0~1) |
paused | 獲取當(dāng)前音頻是否已暫停 |
currentTime | 獲取或設(shè)置當(dāng)前音頻的播放進(jìn)度(單位:秒) |
duration | 獲取當(dāng)前音頻的長(zhǎng)度(單位:秒) |
playbackRate | 獲取或設(shè)置當(dāng)前音頻的播放速度 |
play () | 播放音頻 |
pause () | 暫停音頻 |
點(diǎn)擊查看所有 Media 事件
事實(shí)上 Audio 和 Video 對(duì)象差不多 都屬于 Media
所以如果你會(huì)開(kāi)發(fā)音樂(lè)播放器那么也會(huì)開(kāi)發(fā)視頻播放器了
這里重點(diǎn)說(shuō)一下 timeupdate 事件,這個(gè)事件在音頻播放時(shí)不斷觸發(fā),這個(gè)可以說(shuō)是最有用的事件了
因?yàn)樵诓シ胚^(guò)程中需要不斷的重繪播放器的播放進(jìn)度和已播放時(shí)間
如果有歌詞的話,還需要根據(jù)當(dāng)前的播放時(shí)間去同步歌詞
如果沒(méi)有或者不知道這個(gè)事件的話,你可能會(huì)使用 setInterval 代替
使用 setInterval 的話,會(huì)有兩個(gè)問(wèn)題:
1.重繪時(shí)間到底設(shè)置多少合適?太快了影響性能,太慢了頁(yè)面不同步
2.如果用戶暫停播放了,需要清除定時(shí)器,開(kāi)始播放又要初始化定時(shí)器,太麻煩
(或者偷懶的話可以判斷 paused 時(shí) return ,那么需要不斷的跑一個(gè)空定時(shí)器)
可能做這個(gè)功能的時(shí)候是最好玩的吧 qwq
因?yàn)樵诤芫靡郧?千千靜聽(tīng)那個(gè)年代 我無(wú)聊的時(shí)候就做一下 LRC 歌詞
所以對(duì)這個(gè)功能很敏感 盡量做到最好吧 (′?_?`)
這里主要功能是歌詞解析,歌詞同步的話只要計(jì)算出與當(dāng)前播放時(shí)間最匹配的項(xiàng)元素
然后設(shè)置歌詞面板的滾動(dòng)條位置到當(dāng)前元素的位置即可
常見(jiàn)的時(shí)間標(biāo)簽有以下幾種
[mm:ss] 只有分和秒的時(shí)間標(biāo)簽 [mm:ss:ms] 有分、秒、毫秒的時(shí)間標(biāo)簽 [mm:ss.ms] 有分、秒、毫秒的時(shí)間標(biāo)簽的另一種格式 [mm:ss:ms][mm:ss.ms] 多個(gè)時(shí)間標(biāo)簽共享這一句相同的歌詞
我的思路是:
首先按照行將歌詞文本分割成數(shù)組,再按行進(jìn)行解析
使用正則表達(dá)式匹配出該行的 分、秒、毫秒 和顯示的歌詞文本
將 分、秒、毫秒 都轉(zhuǎn)換成毫秒單位然后加起來(lái)與歌詞文本關(guān)聯(lián)后保存到數(shù)組中,最后需要按照時(shí)間正序排列
那么 當(dāng)前要顯示的歌詞 = 過(guò)濾數(shù)組中 時(shí)間 < 當(dāng)前播放時(shí)間 后的最后一項(xiàng)
private async parseLRC (): Promise{ if (!this.lrc || this.lrc === "loading") return if (this.isURL(this.lrc)) { // 如果歌詞是一個(gè)URL地址則請(qǐng)求該地址獲得歌詞文本 const { data } = await Axios.get(this.lrc.toString()) this.currentLRC = data } else this.currentLRC = this.lrc const reg = /[(d+):(d+)[.|:](d+)](.+)/ const regTime = /[(d+):(d+)[.|:](d+)]/g const regCompatible = /[(d+):(d+)]()(.+)/ const regTimeCompatible = /[(d+):(d+)]/g const regOffset = /[offset:s*(-{0,1}d+)]/ const offsetMatch = this.lrc.match(regOffset) const offset = offsetMatch ? Number.parseInt(offsetMatch[1]) : 0 this.LRC = [] const matchAll = (line: string) => { let match = line.match(reg) || line.match(regCompatible) if (!match) return if (match.length !== 5) return const minutes = Number.parseInt(match[1]) || 0 const seconds = Number.parseInt(match[2]) || 0 const milliseconds = Number.parseInt(match[3]) || 0 const time = (minutes * 60 * 1000 + seconds * 1000 + milliseconds) + offset const text = (match[4] as string).replace(regTime, "").replace(regTimeCompatible, "") if (!text) return // 優(yōu)化:不要顯示空行 this.LRC.push({ time, text }) matchAll(match[4]) // 遞歸匹配多個(gè)時(shí)間標(biāo)簽 } this.currentLRC.replace(/ /g, " ").split(" ").forEach(line => matchAll(line)) // 歌詞格式不支持 if (this.LRC.length <= 0) this.LRC = [{ time: -1, text: "(???*) 抱歉,該歌詞格式不支持" }] else this.LRC.sort((a, b) => a.time - b.time) }
點(diǎn)擊查看完整代碼
總結(jié)完善了原 APlayer 不足的地方:
1.可以響應(yīng)式的隨意控制播放器屬性
2.歌詞同步支持多種時(shí)間標(biāo)簽格式(fix #39)
3.歌詞同步兼容 [offset:0] 標(biāo)簽
4.異步歌詞的支持
5.允許控制播放速度(相同的歌曲用不同的速度聽(tīng)感覺(jué)會(huì)不一樣哦 quq)
6.音量允許拖動(dòng)控制
7.支持注冊(cè)所有 Media 事件
8.保存播放器配置到 localStorage 中,刷新后可以恢復(fù)播放進(jìn)度等信息
并且體驗(yàn)了一把用 TS 寫(xiě) Vue 的快感w
最后 弱弱的:@MoeFE 歡迎各位大佬加入 (??????)??
額..沒(méi)啥要求 頭像要萌要可愛(ài)的?。?/p>
好想有個(gè)大佬能帶我裝逼帶我飛 (? ?_?)?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/84258.html
摘要:前端日?qǐng)?bào)精選三思而后行想提高團(tuán)隊(duì)技術(shù),來(lái)試試這個(gè)套路如何開(kāi)發(fā)一個(gè)插件學(xué)習(xí)筆記塊級(jí)作用域綁定譯文詳解帶來(lái)的個(gè)重大變化中文周二放送畫(huà)圖知乎專欄第期新特性譯配置譯高性能視差滾動(dòng)行代碼構(gòu)建區(qū)塊鏈知乎專欄渲染器修仙之路之拷貝對(duì)象已 2017-07-25 前端日?qǐng)?bào) 精選 SSR 三思而后行想提高團(tuán)隊(duì)技術(shù),來(lái)試試這個(gè)套路!如何開(kāi)發(fā)一個(gè) Atom 插件ES6學(xué)習(xí)筆記:塊級(jí)作用域綁定【譯文】詳解VUE2...
摘要:好看又好用的,專為以為原型,在技術(shù)棧上進(jìn)行實(shí)現(xiàn)。項(xiàng)目早在就已起步,起初是對(duì)的簡(jiǎn)單封裝?,F(xiàn)仍在持續(xù)維護(hù)和更新中。如果你在使用搭建自己心愛(ài)的小站,正想挑選一款好看又好用的音樂(lè)播放器,是少數(shù)不錯(cuò)的選擇。 Vue-APlayer showImg(https://segmentfault.com/img/remote/1460000013797187); showImg(https://segm...
摘要:每次用網(wǎng)易云音樂(lè)客戶端播放聽(tīng)歌的時(shí)候,收藏的歌曲,在我的博客上也可以同步進(jìn)行更新。 最近應(yīng)該發(fā)現(xiàn),我的博客https://blog.codelabo.cn左下角多了一個(gè)音樂(lè)播放器 showImg(https://segmentfault.com/img/remote/1460000016786096?w=1806&h=952); 這個(gè)是怎么實(shí)現(xiàn)的?一起來(lái)看看吧 APlayer 首先我們...
閱讀 812·2021-11-23 09:51
閱讀 2525·2021-10-11 11:10
閱讀 1397·2021-09-23 11:21
閱讀 1182·2021-09-10 10:50
閱讀 952·2019-08-30 15:54
閱讀 3390·2019-08-30 15:53
閱讀 3350·2019-08-30 15:53
閱讀 3262·2019-08-29 17:23