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