基于原生audio,重新封装音频播放组件


由于最近开发 h5 项目需要,需要按设计原稿实现音频播放组件,但原始但音频播放又不符合项目需要,为此重新封装整理出来,为此分享

直接上图看效果

image

直接上源码

在 components 中创建 AudioItem,AudioItem 文件中添加 audioItem.js 文件

/**
 * 音频播放组件
 */
import React, { Component } from "react";

import icon_play from "../Image/music.png"; //这个是项目设计稿定义的图片
import icon_pause from "../Image/stop3.png"; //这个是项目设计稿定义的图片

//时分秒转化为秒
const timeEvent = (e) => {
  var time = e;
  var len = time.split(":");
  if (len.length == 3) {
    var hour = time.split(":")[0];
    var min = time.split(":")[1];
    var sec = time.split(":")[2];
    return Number(hour * 3600) + Number(min * 60) + Number(sec);
  }
  if (len.length == 2) {
    var min = time.split(":")[0];
    var sec = time.split(":")[1];
    return Number(min * 60) + Number(sec);
  }
  if (len.length == 1) {
    var sec = time.split(":")[0];
    return Number(sec);
  }
};

class AudioItem extends Component {
  static defaultProps = {
    src: "", // 音频地址
  };

  state = {
    isCanPlay: false, // 判断音频是否加载完成
    playStatus: false, // 播放状态, true 播放中, false 暂停中,
    duration: 0, // 音频的时长
    currentDuration: 0, // 当前的播放时长
  };

  audioItem = null; // 把dom暴露给外部使用
  audio = new Audio(); // 一个音频对象
  timer = null; // 做一个滑条的防抖
  interval = null; // 定时查询播放时的当前时间

  // 播放音频
  play = () => {
    this.audio.play();
    this.interval = setInterval(() => {
      const time = Math.floor(this.audio.currentTime);
      if (time < this.state.duration) {
        this.setState({
          currentDuration: time,
        });
      } else {
        // 播放结束后,直接重置播放时间,停止播放
        // Toast.info('播放完毕');
        clearInterval(this.interval);
        this.audio.currentTime = 0;
        this.audio.pause();
        this.setState({
          currentDuration: 0,
          playStatus: false,
        });
      }
    }, 1000);
  };

  // 暂停音频
  pause = () => {
    this.audio.pause();
    if (this.interval) {
      clearInterval(this.interval);
    }
  };

  // 播放状态切换
  handlePlayStatusChange = () => {
    const { playStatus, isCanPlay } = this.state;

    if (!playStatus) {
      this.play();
    } else {
      this.pause();
    }
    this.setState({
      playStatus: !playStatus,
    });
  };

  handleSilderChange = (value) => {
    if (this.timer) {
      clearTimeout(this.timer);
    }
    // 0.2s之内没有改动就修改当前的时间,做一个播放的防抖
    this.timer = setTimeout(() => {
      this.pause();
      this.audio.currentTime = value;
      this.setState(
        {
          currentDuration: value,
        },
        () => {
          if (this.state.playStatus) {
            this.play();
          }
        }
      );
    }, 200);
  };

  // 根据秒数,返回对应的xx:xx的时间格式
  getDurationString = (number) => {
    let num = Number(number);
    if (isNaN(num) || num <= 0) {
      return "00:00";
    }
    if (num === Infinity) {
      return "00:00";
    }
    if (num < 60) {
      return `00:${num.toString().padStart(2, 0)}`;
    } else if (num < 3600) {
      const minute = Math.floor(num / 60);
      const second = num % 60;
      return `${minute.toString().padStart(2, 0)}:${second
        .toString()
        .padStart(2, 0)}`;
    } else {
      const hour = Math.floor(num / 3600);
      const minute = Math.floor((num - hour * 3600) / 60);
      const second = num - hour * 3600 - minute * 60;
      return `${hour.toString().padStart(2, 0)}:${minute
        .toString()
        .padStart(2, 0)}:${second.toString().padStart(2, 0)}`;
    }
  };

  init = (props) => {
    const { src } = props || this.props;
    if (!src) {
      return;
    }
    this.audio.preload = "automatic";
    this.audio.src = src;
    this.audio.load();
    // 监听音频的时长是否获取到了
    this.audio.ondurationchange = () => {
      const duration = Math.floor(this.audio.duration);
      this.setState({
        duration,
      });
    };
    // 监听音频是否可以播放了
    this.audio.oncanplay = () => {
      const duration = Math.floor(this.audio.duration);
      this.setState({
        duration,
        isCanPlay: true,
      });
    };
  };

  componentDidMount() {
    this.init();
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.src !== this.props.src) {
      this.init(nextProps);
    }
  }

  componentWillUnmount() {
    if (this.timer) {
      clearTimeout(this.timer);
    }
    if (this.interval) {
      clearInterval(this.interval);
    }
    if (this.audio) {
      this.audio.currentTime = 0;
      this.audio.pause();
    }
  }

  render() {
    const { playStatus, duration } = this.state;
    const btn_img = playStatus ? icon_pause : icon_play;
    const durationStr = this.getDurationString(duration);
    return (
      <div ref={(audioItem) => (this.audioItem = audioItem)}>
        <div
          onClick={this.handlePlayStatusChange}
          style={{
            display: "flex",
            justifyContent: "space-between",
            width:
              timeEvent(durationStr) < 5
                ? "100px"
                : timeEvent(durationStr) < 10
                ? "150px"
                : timeEvent(durationStr) < 15
                ? "175px"
                : "200px",
            height: "36px",
            background: "#FFB91B",
            borderRadius: "6px",
          }}
        >
          <div>
            <img
              src={btn_img}
              alt="icon"
              style={{
                paddingTop: "8.5px",
                paddingLeft: "9px",
              }}
            />
          </div>
          <div
            style={{
              width: "21px",
              height: "14px",
              fontSize: "12px",
              fontFamily: "SFUIDisplay-Regular, SFUIDisplay",
              fontWeight: "400",
              color: "#222222",
              marginTop: "3px",
              marginRight: "2px",
              lineHeight: "14px",
              padding: "8px 9px 2px 2px",
            }}
          >
            {timeEvent(durationStr) + 1}"
          </div>
        </div>
      </div>
    );
  }
}

export default AudioItem;

使用

import AudioItem from "../../components/AudioItem/audioItem";

<AudioItem src={"音频地址"} />;

文章作者: BiLiang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 BiLiang !
评论
 上一篇
React 事件冒泡及处理 React 事件冒泡及处理
在开发项目中,我有一个列表要两个事件,点击列表大框,跳转详情,点击列表其他按钮,跳转其他页面,然而点击其他按钮时,会先跳转详情然后在到其他页面。为此总结一下
2021-04-29
下一篇 
项目中常用公共方法总结 项目中常用公共方法总结
由于项目需要,会经常抽离出一些公共方法,为此我把项目实战中长用到的公共方法总结下来。
2021-04-29
  目录