音频播放组件

组件代码

 
import React, {Component} from 'react';
import {Popover} from 'antd';
import moment from 'moment';
import style from './style.less';
 
import playAudio from '../../../images/player.png';
import pauseAudio from '../../../images/icon_play_stop.png';
import onMuteAudio from '../../../images/mute.png';
import onNoMuteAudio from '../../../images/no-mute.png';
import download from '../../../images/download.png';
 
class index 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);
  const minite = Math.floor(time / 60);
  return `${minite}:${second >= 10 ? second : `0${second}`}`;
 }
 handleDownloadClick = () => {
  if (!this.props.src) return;
  const url = this.props.src;
  let request;
  const ishttps = document.location.protocol == 'https:';
  if (ishttps && url) {
   url.split('://');
   request = `https://${url.split('://')[1]}`;
  } else {
   request = url;
  }
  const text = `${this.props.DownloadName}.wav`;
  this.courseDownload(request, text);
 };
 // 实现js下载文件修改文件名失效问题 start
 courseDownload = (url, filename) => {
  const that = this;
  this.getBlob(url, function (blob) {
   that.saveAs(blob, filename);
  });
 };
 
 getBlob = (url, cb) => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  xhr.responseType = 'blob';
  xhr.onload = function () {
   if (xhr.status === 200) {
    cb(xhr.response);
   }
  };
  xhr.send();
 };
 /**
  * 保存
  * @param  {Blob} blob
  * @param  {String} filename 想要保存的文件名称
  */
 saveAs = (blob, filename) => {
  if (window.navigator.msSaveOrOpenBlob) {
   navigator.msSaveBlob(blob, filename);
  } else {
   const link = document.createElement('a');
   const body = document.querySelector('body');
 
   link.href = window.URL.createObjectURL(blob);
   link.download = filename;
 
   // fix Firefox
   link.style.display = 'none';
   body.appendChild(link);
 
   link.click();
   body.removeChild(link);
 
   window.URL.revokeObjectURL(link.href);
  }
 };
 // 该视频已准备好开始播放
 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
   });
  }
  const progressTime = (100 * this.state.currentTime) / audio.duration;
 
  this.inputDom.style.background = `linear-gradient(to right,  #007cef 0%, #007cef ${parseInt(progressTime, 10)}%, transparent ${parseInt(progressTime, 10)}%, transparent 100%)`;
  if (audio.currentTime === audio.duration) {
   this.setState({
    isPlay: false
   });
   this.inputDom.style.background = `linear-gradient(to right,  #007cef 0%, #007cef 100%, transparent 100%, transparent 100%)`;
  }
 };
 
 changeVolume = e => {
  const {value} = e.target;
  const {id} = this.props;
  const audio = document.getElementById(`audio${id}`);
  audio.volume = value / 100;
 
  this.setState({
   volume: value,
   isMuted: !value
  });
 };
 
 // 倍速播放
 changePlayRate = num => {
  this.audioDom.playbackRate = num;
  this.setState({
   playRate: num
  });
 };
 
 render() {
  const {src, id} = this.props;
 
  // const {isPlay, isMuted, volume, allTime, currentTime, rateList, playRate} = this.state;//倍数
  const {isPlay, isMuted, volume, allTime, currentTime} = this.state;
 
  return (
   <div className={style.AudioComponent}>
    <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>
 
    {isPlay ? (
     <div className={style.isPlayBox} onClick={this.pauseAudio}>
      <img src={pauseAudio} />
     </div>
    ) : (
     <div className={style.isPlayBox} onClick={this.playAudio}>
      <img src={playAudio} />
     </div>
    )}
 
    <div className={style.audioInputBox}>
     <div className={style.AudioBox}>
      <input className={style.AudioInput} ref={dom => (this.inputDom = dom)} type="range" step="0.01" max={allTime} value={currentTime} onChange={this.changeTime} />
     </div>
     <span className={style.timeBox}>{`${this.formatSecond(currentTime)}/${this.formatSecond(allTime)}`}</span>
    </div>
 
    <div className={style.volumeBox}>
     <Popover
      overlayClassName={'volumeBox'}
      trigger="click"
      content={
       <div className="volume">
        <div onClick={this.onMuteAudio}>
         <img src={!isMuted ? onMuteAudio : onNoMuteAudio} />
        </div>
        <input type="range" onChange={this.changeVolume} value={isMuted ? 0 : volume} />
       </div>
      }>
      <span>
       <img src={onNoMuteAudio} />
      </span>
     </Popover>
    </div>
    <div onClick={this.handleDownloadClick}>
     <img src={download} alt="" />
    </div>
    {/* <div>
     <span>倍速播放:</span>
     {rateList &&
      rateList.length > 0 &&
      rateList.map(item => (
       <button
        key={item}
        style={
         playRate === item
          ? {
            border: '1px solid #188eff',
            color: '#188eff'
            }
          : null
        }
        onClick={() => this.changePlayRate(item)}>
        {item}
       </button>
      ))}
    </div> */}
   </div>
  );
 }
}
 
export default index;
 

组件样式

.AudioComponent {
 display: flex;
 align-items: center;
 background-color: #fff;
 width: 100%;
 max-width: 500px;
 height: 32px;
 padding: 20px;
 border-radius: 16px;
 
 > div {
  margin-right: 12px;
 
  &:last-child {
   margin-right: 0;
  }
 }
 
 .content {
  display: flex;
  justify-content: center;
  align-items: center;
 }
 
 .timeBox {
  font-size: 12px;
  font-weight: 400;
  color: #bababa;
  margin-left: 4px;
 }
 
 .isPlayBox {
  img {
   width: 20px;
   height: 20px;
   .content;
  }
 }
 
 .audioInputBox {
  display: flex;
  align-items: center;
 }
 
 .AudioBox {
  background-color: #dfdfdf;
  position: relative;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  width: 200px;
 
  .AudioInput {
   position: absolute;
   top: 0;
   left: 0;
   width: 100%;
   background: linear-gradient(to right, #007cef, transparent 0%, transparent);
   background-size: 100% 100%;
 
   &[type='range'] {
    width: 100%;
    -webkit-appearance: none;
    height: 8px;
    border-radius: 4px;
    background: -webkit-linear-gradient(#007cef, #007cef) no-repeat transparent;
    background-size: 0% 100%; /* 因为周期默认value=0.50正好占50% */
   }
 
   &[type='range']::-webkit-slider-thumb:hover {
    background-color: transparent;
   }
 
   &[type='range']::-webkit-slider-runnable-track {
    border-radius: 10px; /*将轨道设为圆角的*/
   }
 
   &[type='range']::-webkit-slider-thumb {
    position: relative;
    left: -16px;
    -webkit-appearance: none;
    background-color: #007cef;
    width: 16px;
    height: 8px;
    cursor: pointer;
   }
  }
 }
}
 
:global {
 .volumeBox {
  // transform: rotate(-90deg);
 
  // .ant-popover-arrow-content {
  //  display: none;
  // }
  .ant-popover-inner-content {
   padding: 0 16px;
  }
 
  .volume {
   display: flex;
 
   > div {
    padding: 5px;
   }
  }
 }
}
 

组件引用

import Audio from "./AudioPlay";
 
 <Audio
  src={
    "https://xxx.mp3"
  }
  id={123}
/>