react封装audio组件重置样式

1,348 阅读2分钟

为了满足产品的需求,为了实现多种浏览器音频播放器的样式保持一直,动手封装一个简单的播放组件

功能和效果

  • 时长
  • 播放进度条
  • 倍速
  • 调音
  • 暂停播放
  • 点击下载

效果如下,可以根据自己的需要更改样式

2022-03-18 09.57.06.gif

注意事项

  • 若是单页面应用,切换页面时,应该暂停当然audio的播放

引入组件使用

<AudioPlay
  id="record"
  src={http:xxx.mp3}
></AudioPlay>

根据URL实现下载文件的工具方法

import { message } from "antd";
const lyh = {}
lyh.downUrl = (fileName, url) => {
  message.info("正在下载中...");
  let x = new XMLHttpRequest();
  x.open("GET", url, true);
  x.responseType = "blob";
  x.onload = function(e) {
    let blob = x.response;
    if ("msSaveOrOpenBlob" in navigator) {
      //IE导出
      window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else {
      let a = document.createElement("a");
      a.download = fileName;
      a.href = URL.createObjectURL(blob);
      document.querySelector("body").appendChild(a);
      a.click();
      document.querySelector("body").removeChild(a);
      message.info("下载成功");
    }
  };
  x.send();
};

export default lyh;

react组件代码-AudioPlay.js

import React, { Component } from "react";
import "./style/audio.less";
import lyh from "../../../../lib/lyh";

import { Input, Icon, Dropdown, Menu, Button, Slider } from "antd";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      rateList: [1.0, 1.25, 1.5, 2.0],
      playRate: 1.0,
      isPlay: false,
      isMuted: false,
      volume: 100,
      allTime: 0,
      currentTime: 0
    };
  }

  componentDidMount() {}

  formatSecond(time) {
    const second = Math.floor(time % 60);
    let minite = Math.floor(time / 60);
    return `${minite}:${second >= 10 ? second : `0${second}`}`;
  }

  // 该视频已准备好开始播放
  onCanPlay = () => {
    const { id } = this.props;
    const audio = document.getElementById(`audio${id}`);
    this.setState({
      allTime: audio.duration
    });
  };

  playAudio = () => {
    const { id } = this.props;
    const audio = document.getElementById(`audio${id}`);
    audio.play();
    this.setState({
      isPlay: true
    });
  };

  pauseAudio = () => {
    const { id } = this.props;
    const audio = document.getElementById(`audio${id}`);
    audio.pause();
    this.setState({
      isPlay: false
    });
  };

  onMuteAudio = () => {
    const { id } = this.props;
    const audio = document.getElementById(`audio${id}`);
    this.setState({
      isMuted: !audio.muted
    });
    audio.muted = !audio.muted;
  };

  changeTime = e => {
    const { value } = e.target;
    const { id } = this.props;
    const audio = document.getElementById(`audio${id}`);
    this.setState({
      currentTime: value
    });
    audio.currentTime = value;
    if (value === audio.duration) {
      this.setState({
        isPlay: false
      });
    }
  };

  // 当前播放位置改变时执行
  onTimeUpdate = () => {
    const { id } = this.props;
    const audio = document.getElementById(`audio${id}`);

    this.setState({
      currentTime: audio.currentTime
    });
    if (audio.currentTime === audio.duration) {
      this.setState({
        isPlay: false
      });
    }
  };

  changeVolume = value => {
    const { id } = this.props;
    const audio = document.getElementById(`audio${id}`);
    audio.volume = value / 100;

    this.setState({
      volume: value,
      isMuted: !value
    });
  };
  onChangeMutedStatus = a => {
    const { id } = this.props;
    const audio = document.getElementById(`audio${id}`);

    this.setState({
      isMuted: !audio.muted
    });
    audio.muted = !audio.muted;
  };

  // 倍速播放
  changePlayRate = ({ item, key, keyPath, domEvent }) => {
    this.audioDom.playbackRate = key;
    this.setState({
      playRate: key
    });
  };

  render() {
    const { src, id } = this.props;

    const {
      isPlay,
      isMuted,
      volume,
      allTime,
      currentTime,
      rateList,
      playRate
    } = this.state;

    return (
      <div className="lyh-audio-wrap">
        <span className="lyh-audio-voice">
          <i className="lyh-audio-first" />
          <i className="lyh-audio-second" />
          <i className="lyh-audio-three" />
          <i className="lyh-audio-four" />

          <i className="lyh-audio-first" />
          <i className="lyh-audio-second" />
          <i className="lyh-audio-three" />
          <i className="lyh-audio-four" />

          <i className="lyh-audio-first" />
          <i className="lyh-audio-second" />
          <i className="lyh-audio-three" />
          <i className="lyh-audio-four" />

          <i className="lyh-audio-first" />
          <i className="lyh-audio-second" />
          <i className="lyh-audio-three" />
          <i className="lyh-audio-four" />

          <i className="lyh-audio-first" />
          <i className="lyh-audio-second" />
          <i className="lyh-audio-three" />
          <i className="lyh-audio-four" />
        </span>
        <audio
          id={`audio${id}`}
          src={src}
          ref={audio => {
            this.audioDom = audio;
          }}
          preload={"auto"}
          onCanPlay={this.onCanPlay}
          onTimeUpdate={this.onTimeUpdate}
        >
          <track src={src} kind="captions" />
        </audio>

        <div className="audio-control-wrap lyh-fsb">
          <span>
            {this.formatSecond(currentTime) + "/" + this.formatSecond(allTime)}
          </span>
          <input
            type="range"
            step="0.01"
            max={allTime}
            value={currentTime}
            onChange={this.changeTime}
            style={{ flex: 1, margin: "0 10px" }}
          />

          <div className="audio-control lyh-fsb" style={{ width: 122 }}>
            <Dropdown
              overlay={
                <Menu onClick={this.changePlayRate}>
                  {rateList.map(v => (
                    <Menu.Item key={v}>{v}x</Menu.Item>
                  ))}
                </Menu>
              }
              placement="topCenter"
            >
              <div
                style={{
                  cursor: "pointer"
                  // width: 22
                }}
              >
                {playRate}x
              </div>
            </Dropdown>

            <Dropdown
              overlay={
                <div
                  style={{
                    display: "inline-block",
                    height: 66,
                    paddingBottom: 8
                  }}
                >
                  <Slider
                    vertical
                    onChange={this.changeVolume}
                    value={isMuted ? 0 : volume}
                  />
                </div>
              }
              placement="topCenter"
            >
              <div
                className="soud-wrap lyh-fjic"
                onClick={this.onChangeMutedStatus}
              >
                <Icon type="sound" className="ico" />
                {isMuted ? <div className="muted-ico"></div> : ""}
              </div>
            </Dropdown>

            {isPlay ? (
              // theme="twoTone"
              <Icon
                type="pause-circle"
                className="ico"
                onClick={this.pauseAudio}
              />
            ) : (
              <Icon
                type="play-circle"
                className="ico"
                onClick={this.playAudio}
              />
            )}
            <Icon
              type="download"
              className="ico"
              onClick={a => lyh.downUrl("通话录音", src)}
            />
          </div>
        </div>
      </div>
    );
  }
}

export default App;

audio.less样式文件

.audio-control-wrap {
  .ico {
    font-size: 18px;
    cursor: pointer;
  }

  .soud-wrap {
    width: 20px;
    height: 20px;
    position: relative;

    .muted-ico {
      position: absolute;
      top: 0;
      left: 10px;
      width: 2px;
      height: 100%;
      background-color: #272525;
      transform: rotate(130deg);
      z-index: 99;
    }
  }
}

.lyh-audio-voice {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: 100%;
  height: 15px;
  height: 38px;

  i {
    display: inline-block;
    border-left: 2px solid #10ea14;
    margin: 0 1px;
    position: relative;

    &.lyh-audio-first {
      height: 8px;
      height: 24px;
      animation: first 0.5s linear 0s infinite alternate;
    }

    &.lyh-audio-second {
      height: 4px;
      height: 12px;
      animation: second 1s linear 0s infinite alternate;
    }

    &.lyh-audio-three {
      height: 6px;
      height: 18px;
      animation: three 0.8s linear 0s infinite alternate;
    }

    &.lyh-audio-four {
      height: 12px;
      height: 36px;
      animation: four 0.6s linear 0s infinite alternate;
    }
  }

  @keyframes first {
    0% {
      height: 4px;
    }

    50% {
      height: 8px;
    }

    100% {
      height: 10px;
    }
  }

  @keyframes second {
    0% {
      height: 12px;
    }

    50% {
      height: 9px;
    }

    100% {
      height: 7px;
    }
  }

  @keyframes three {
    0% {
      height: 14px;
      height: 24px;
    }

    50% {
      height: 12px;
      height: 32px;
    }

    100% {
      height: 10px;
      height: 20px;
    }
  }

  @keyframes four {
    0% {
      height: 10px;
      height: 30px;
    }

    50% {
      height: 8px;
      height: 24px;
    }

    100% {
      height: 5px;
      height: 15px;
    }
  }
}