1. 程式人生 > >Audio 標籤的使用和自己封裝一個強大的React音樂播放器

Audio 標籤的使用和自己封裝一個強大的React音樂播放器

原文地址:https://www.dodoblog.cn/blog?id=5be84d5c70b2b617f27a4610

 

這篇文章主要介紹一下部落格裡的這個音樂播放器是怎麼寫的

為了更好的表達高深的東西,還是需要先強調點簡單的東西 

Audio元素的屬性

paused 是否暫停,不可以修改

audio.paused = true // 錯誤
audio.pause() // 正確

currentTime 當前播放的時間,也可以用來改變進度,以下兩行程式碼是等價的

audio.currentTime = 10
audio.play(10)

duration 獲取當前載入好的音樂的時間

loop 是否單曲循魂

mute 是否靜音

volume 音量大小,數值是0 到 1 

 

Audio元素的事件

play和pause

播放和暫停的時候觸發的事件

 

seeking和seeked

這裡的快進是時間突然到某一個點上,不是倍速播放,快進開始和快進完成分別觸發一個不同事件,因為快進了可能需要載入資料,需要一點點時間

比如改變currentTime

 

loadstart,loadeddata和canplay

開始加,載入了一幀資料,載入到足夠播放的資料

 

ended

結束的時候,結束了播放下一首用

 

還有一些事件,比如progress, volumnchange 這些方法,隨便搜一下就可以知道了,大家需要自行搜去,正常情況下這些就夠用了。 

 

React播放器

引數

  <MusicPlayer
    getAudio={audio => this.audio = audio}
    musics={store.musicStore.currentList.songs}
  
/>

 

為了方便啟用,核心的引數只有兩個

getAudio,和ref一樣,可以返回當前的audio元素 musics是一個音樂的列表

因為在audio元素的基礎上開發,所以幾乎所有的事件其實也都是在audio上通過監聽獲取到,所以把audio元素丟擲可以減少很多配置

 

傳入一個數組 這個陣列是一個音樂列表的資訊

每個物件應該包括 id,封面圖連結,歌曲連結,歌詞連結,歌唱家,歌曲名稱

 

樣式

 

我寫的這個播放器是這樣的,很簡單的樣子,基本功能都有,沒有迴圈播放和隨機播放的設定,其實也很簡單

 

 

 

 

這個播放器的主要元素包括

封面,歌曲列表,名稱,歌詞,進度,上一首下一首,還有封面上那個錢幣一樣的按鈕是播放暫停

然後側面的兩個按鈕可以控制播放器的顯示隱藏和歌曲列表的顯示隱藏

具體的UI大家想象著寫就好了,反正我寫的也不算好看

 

留出那幾個控制的按鈕繫結事件就好了


初始化

  componentDidMount() {
    const musics = this.props.musics
    const audio = this.$audio.current
    this.setState({
      currentIndex: musics && musics.findIndex(item => item.id === window.localStorage.getItem('current-music-id')) || 0,
    })

    audio.addEventListener('play', this.handlePlay)
    audio.addEventListener('pause', this.handlePause)
    this.props.getAudio && this.props.getAudio(audio)
  }

 

初始化的時候,

1. 添加了事件監聽,這樣當外部改變audio的狀態也會改變播放器元件的狀態

2. 獲取localstorage裡的音樂,這個很友好,可以重新整理仍然是那一首歌

3. 丟擲audio元素

 

播放和暫停

  handlePlay = () => {
    const music = this.props.musics[this.state.currentIndex]
    const audio = this.$audio.current

    this.handleLoadLrc()
    this.setState({ paused: false })

    window.localStorage.setItem('current-music-id', music.id)
    audio.play(audio.currentTime)

    this.timer = setInterval(() => {
      const { currentTime, duration } = audio
      this.setState({ currentTime, duration })
    }, 100)
  }

  handlePause = () => {
    this.setState({ paused: true })
    this.$audio.current.pause()
    this.timer && clearInterval(this.timer)
  }

 

注意

1. currentIndex表示當前的播放的音樂的index

2. 我們設定了pause的屬性 是記錄播放器的當前狀態,audio的狀態改變不能實時反應去渲染dom,所以需要用自己的state記錄。

3. play需要傳入當前的audio時間,這樣暫停播放不會重新開始,而切換後currentTime也會歸0

4. 動態記錄了currentTime和duration,並且用計時器不對獲取,也是因為我們並沒有辦法實時獲取時間反映到進度條上

 

上一首,下一首和切換

  handleNext = () => {
    let currentIndex = this.state.currentIndex + 1
    if (currentIndex >= this.props.musics.length) {
      currentIndex = 0
    }

    this.setState({currentIndex }, this.handlePlay)
  }

  handlePrev = () => {
    let currentIndex = this.state.currentIndex - 1
    if (currentIndex < 0) {
      currentIndex = this.props.musics.length - 1
    }

    this.setState({currentIndex }, this.handlePlay)
  }

  handleToggle = currentIndex => {
    this.setState({currentIndex }, this.handlePlay)
  }

 

 

上一首就是index - 1然後播放,如果index = 0 那麼index為最後一個

下一首就是index +1然後播放,如果index已經最大 那麼index重置0

切換就是接受一個index 改變後播放

這三個超級簡單,不多解釋

 

快進

  handlePlayFrom = e => {
    const audio = this.$audio.current
    const { left, width } = e.target.getBoundingClientRect()
    const clickPos = (e.clientX - left) / width
    const time = audio.duration * clickPos
    if (!time) return false
    audio.currentTime = time
  }

 

 

這個稍微考驗些關於dom位置判斷的功底

通過獲取進度條的寬度,點選的位置,然後計算出一個進度的百分比,最後根據這個百分比和總的音樂時長計算出點選點的時間

 

載入歌詞

handleLoadLrc = () => {
    const request = new XMLHttpRequest();
    const url = this.props.musics[this.state.currentIndex].lrc
    request.open('GET', url, true);
    request.onload = () => {
      this.lyricStr = request.response
    }
    request.send();
  } 

播放的時候回去載入歌詞,就是一個ajax請求,返回的歌詞一般都是這樣的格式

 

 

看起來像個數組,其實是一個字串,然後我們可以通過正則將其拆分成一個時間 歌詞一一對應的陣列,這個是最麻煩的一點,但其實並沒有什麼技術複雜度,主要就是處理字串通過split拆分成陣列就好

然後根據當前的currentTime獲取需要顯示的那句歌詞

不細節分析了,有需要的可以在我的github上看

 

切換歌單

  componentDidUpdate(nextProps) {
    if (nextProps.musics !== this.props.musics) {
      this.setState({ currentIndex: 0 }, this.handlePlay)
    }
  }

 

當音樂列表發生改變的時候,重新播放就好了

 

播放器的顯示隱藏

動態新增一個class去隱藏整個播放器就好啦,fixed定位,很容易搞定的

播放器的基本功能就這麼多啦,當然還有一些,比如設定播放模式,依次還是隨機,是否單曲迴圈等等,這些其實都很簡單。

 

具體的程式碼

戳這裡 獲取播放器的程式碼,有需要的自取

 

總結,在audio元素的基礎上開發是比較簡單的,但是這也有很多不好的地方,比如說,別人找到audio元素,然後加一個controls屬性,就可以下載音樂了。

真正強大的是Audio API,W3C提供了操作音訊的一系列api 可以實現更好的音訊buffer 播放效果,甚至通過analyser分析音訊進行mix混音以及音樂效果的改變。

還可以寫好玩的動畫效果,比如

 這些的前提當然是需要對音樂有一定的瞭解,有興趣的小夥伴可以研究一下哦,這是很有趣的一個方向。