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]);

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