把網頁變成可在背景運行的音樂播放器
這個方法只有在 Android 上面可行, iOS 上面只要瀏覽器被最小化的時候, 瀏覽器就會被暫停, 所以基本上沒有辦法讓 PWA 或是 Web Application 在 iOS 上面還可以在背景運行, 因此請謹慎服用。
自動播放下一首
要實現自動播放下一首並不困難,其實就是我們要用關掉前的那個 Audio 實例來播放音樂,如果在關掉之後使用了new Audio()
基本上就不行了,考慮以下的程式碼,因為我是用 Vuex 在管理狀態以及實例,因此以下我就直接使用 Vuex 來寫:
export default new Vuex.Store({
state: {
audio: new Audio(),
playNextHooked: false
},
actions: {
play({state, dispatch}, payload) {
// 為了避免 ended 事件被多次 hook
// 所以在這裡加上一個 playNextHooked
// 使得 stats.audio 的 ended 事件只會被 hook 一次
if(!state.playNextHooked) {
state.playNextHooked = true;
state.audio.addEventListener("ended", () => {
// 發送 playNext 指令
dispatch("playNext")
})
}
// 直接將要播放的音樂連結置換當前正在播放的音樂
// 我的猜測是只有當在瀏覽器處於前台狀態
// 那時候的 audio 才有被播放的權限,
// 而當瀏覽器最小化之後才 new Audio() 的話,
// 新的 Audio 實例就沒有辦法被播放,
// 也許跟安全性也有一些關係,
// 否則如果在背景可以任意的 new Audio() 並且 play() 的話
// 完全就可以做一個惡意網站 然後有時候就突然在你手機背景播音樂
state.audio.src = payload.url;
// 時間歸零
state.audio.currentTime = 0;
// 播放
state.audio.play();
},
playNext({dispatch}) {
// ... 自訂策略找出下一首要播放的曲目
// 發送播放指令
dispatch("play", {url: "https://foo.bar/music.url"})
}
}
})
狀態列顯示音樂資訊並控制
顯示音樂資訊的部份就要使用到 MeidaSession 的功能了,MediaSession 可以協助開發者將當前的多媒體資訊顯示到手機的通知列中,並且可以在螢幕鎖定後不用解鎖就可以控制多媒體:
if('mediaSession' in navigator) {
// eslint-disable-next-line no-undef
navigator.mediaSession.metadata = new MediaMetadata({
title: "音樂標題",
artist: "音樂家",
album: "專輯名稱",
artwork: [{ src: "專輯封面或音樂的圖片連結" }]
})
navigator.mediaSession.setActionHandler('play', () => {
// 控制繼續播放音樂
// 也可以直接呼叫 vuex 的 action,如下:
// dispatch("continuePlay");
})
navigator.mediaSession.setActionHandler('pause', () => {
// 控制暫停當前播放的音樂
// 也可以直接呼叫 vuex 的 action,如下:
// dispatch("pauseCurrent");
})
navigator.mediaSession.setActionHandler('nexttrack', () => {
// 控制暫停當前播放的音樂
// 也可以直接呼叫 vuex 的 action,如下:
// dispatch("playNext");
})
// 其他的 Handler 還有:
// seekbackward 快退
// seekforward 快進
// previousTrack 前一首曲子
}
以範例的程式碼來說,如果我只有設定play
、pause
以及nexttrack
這三個 Handler 的話,狀態列也就只會顯示有設定 Handler 的按鈕,如下: