audio自定義樣式

背景:audio的control樣式不符合產品需求,UI設計後需要滿足需求,自定義樣式唄,還能咋地。

UI設計圖如下:

拿到需求先別慌,具體拆分功能如下

  • 第一,開始播放錄音和停止播放錄音的按鈕
  • 第二,audio元素
  • 第三,播放音頻的進度條
  • 第四,模擬錄音的實時播放時間以及音頻總時長

接下來讓我們依次來說明:

1、引入需要用到的組件以及依賴

說明:項目中UI大框架用的是antd-design(React)

import { Icon, Progress } from 'antd';
import moment from 'moment';
2、開始播放錄音和停止播放錄音的按鈕
  • 提前定義變量
 const [audio, setAudio] = useState({
    isPausing: true,
    ref: createRef(),
  });
  • UI實現 直接使用antd的Icon來實現按鈕樣式,根據最先定義的isPausing狀態來控制是播放還是停止錄音

使用ICON的原因,停止的caret-right居中的時候看着總是不居中,因此我設置了一個小小的padding(less.audioBtnPadding)

<div className={less.audioBtn + ' ' + (audio.isPausing ? less.audioBtnPadding : '')} onClick={() => audioControlHandle()}>
    <Icon type={audio.isPausing ? 'caret-right' : 'pause'} />
</div>
  • audioControlHandle實現(核心)

寫之前需要思考一下,這個函數要實現的功能是什麼:

  • 1、點擊按鈕的時候需要根據轉換狀態,停止的話就打開,打開的話就停止;
  • 2、獲取到audio元素,根據audio的狀態去修改,打開的話就關閉currentDom.pause(),停止的話就打開currentDom.play();
  • 3、實現進度條的功能
  • 4、獲取音頻的實時時間以及音頻總時長(音頻總時長錄音結束的時候就可以拿到,即應是提前就獲取到的)
const audioControlHandle = useCallback(() => {
    const cAudio = _.cloneDeep(audio);
    cAudio.isPausing = !cAudio.isPausing;
    setAudio(cAudio);
    const currentDom = cAudio.ref.current;
    if (cAudio.isPausing) {
      currentDom.pause();
    } else {
      currentDom.play();
      cAudio.playTimer = setInterval(() => {
        if (currentDom.paused) {
          clearInterval(cAudio.playTimer);
          if (Math.floor(currentDom.currentTime) >= Math.floor(currentDom.duration)) {
            cAudio.isPausing = true;
            setAudio(cAudio);
          }
        }
        setCurrentTimes(moment(Math.floor(currentDom.currentTime * 1000)).format('mm:ss'));
        setProgressPercent(Math.floor(((currentDom.currentTime / currentDom.duration) * 100)));
      }, 300);
    }
  }, [setAudio, setCurrentTimes, setProgressPercent, audio]);

3、audio元素

做再多的外在裝飾,其實真正實現播放音頻的還是audio元素,所以必須有audio元素做基礎。 src表示音頻的url,ref幫助我們獲取到audio元素

<audio src={audioUrl} ref={audio.ref} />

4、播放音頻的進度條

可以直接使用antd的process組件,非常方便,設置一些屬性即可

<Progress
      percent={progressPercent}
      showInfo={false}
      strokeWidth={7}
      strokeColor="#B6B6B6"
      trailColor="#F1F1F1"
    />
5、模擬audio播放時候顯示的實時時間以及音頻時長

提前定義變量

const [currentTimes, setCurrentTimes] = useState('00:00');
const [audioDuration, setAudioDuration] = useState(0);

點擊播放按鈕的時候獲取音頻實時時間格式化之後用來顯示

 <div className={less.audioTime}>
    {currentTimes}
    /
    {moment(audioDuration).format('mm:ss')}
  </div>
6、說了這麼多,直接上代碼

Html:

<div className={less.recordingPlay}>
  <div className={less.audio}>
    <div className={less.audioBtn + ' ' + (audio.isPausing ? less.audioBtnPadding : '')} onClick={() => audioControlHandle()}>
      <Icon type={audio.isPausing ? 'caret-right' : 'pause'} />
    </div>
    <audio src={audioUrl} ref={audio.ref} />
    <Progress
      percent={progressPercent}
      showInfo={false}
      strokeWidth={7}
      strokeColor="#B6B6B6"
      trailColor="#F1F1F1"
    />
  </div>
  <div className={less.audioTime}>
    {currentTimes}
    /
    {moment(audioDuration).format('mm:ss')}
  </div>
</div>

css(less實現):

.recordingPlay{
    width: 100%;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: space-between;

    .audio{
      width: 715px;
      height: 44px;
      display: flex;
      justify-content: space-between;
      align-items: center;

      audio{
        width: 715px;
        height: 44px;
      }

      .audioBtn{
        width: 44px;
        height: 44px;
        border:3px solid rgba(4,32,68,1);
        border-radius: 50%;
        font-size: 30px;
        color: rgba(4,32,68,1);
        display: flex;
        justify-content: center;
        align-items: center;
        cursor: pointer;
        margin-right: 23px;
      }
      .audioBtnPadding{
        padding-left: 4px;
      }
    }
    .audioTime{
      width: 112px;
      height: 22px;
      font-size:16px;
      font-family:PingFangSC-Medium,@fontFamily;
      font-weight:500;
      color:rgba(4,32,68,1);
      line-height:22px;
    }
    .button{
      width:122px;
      height:36px;
      background:rgba(81,135,213,1);
      border-radius:2px;
      border: 0px;
      font-size:14px;
      font-family:PingFangSC-Regular,@fontFamily;
      font-weight:400;
      color:rgba(255,255,255,1);
      line-height:20px;
    }
  }

JS:

 
 const audioControlHandle = useCallback(() => {
    const cAudio = _.cloneDeep(audio);
    cAudio.isPausing = !cAudio.isPausing;
    setAudio(cAudio);
    const currentDom = cAudio.ref.current;
    if (cAudio.isPausing) {
      currentDom.pause();
    } else {
      currentDom.play();
      cAudio.playTimer = setInterval(() => {
        if (currentDom.paused) {
          clearInterval(cAudio.playTimer);
          if (Math.floor(currentDom.currentTime) >= Math.floor(currentDom.duration)) {
            cAudio.isPausing = true;
            setAudio(cAudio);
          }
        }
        setCurrentTimes(moment(Math.floor(currentDom.currentTime * 1000)).format('mm:ss'));
        setProgressPercent(Math.floor(((currentDom.currentTime / currentDom.duration) * 100)));
      }, 300);
    }
  }, [setAudio, setCurrentTimes, setProgressPercent, audio]);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章