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混音以及音樂效果的改變。

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM