react+typescript使用ref实现自定义的audio标签

react+typescript使用ref实现自定义的audio标签

react+ts比较严格,一般的ref会一直提示没有这个属性,一定都要声明后才能使用

标签上的ref要用大括号的形式:

<audio ref={
     this.audio} src={
     url} controls>
   你的浏览器不支持音频播放哦!(;´༎ຶД༎ຶ`)
</audio>

在class里面的最外侧定义audio的类型

audio: React.RefObject<any>;

然后在constructor这个生命周期中创建ref句柄

this.audio = React.createRef();

打印一下this.audio:

  componentDidMount() {
     
    console.log(this.audio);
  }

打印结果
会发现有一个current的属性,所有原生js的方法和属性全都在这个属性里面,所以我们要进行原生的js操作要加上这个属性,否则会为空:

this.audio.current.play();

以上就是基本的技术点,下面进入demo

html结构:进度条外框outer包裹着进度条内核inner

<div
   className="outer"
   ref={
     this.outer}
   onTouchMove={
     e => this.audioX(e)}
   onTouchStart={
     e => this.audioX(e)}
   >
     <div className="inner" ref={
     this.inner}>
         <div className="innerBtn"></div>
     </div>
</div>

css样式如下:

.outer{
     
    width: 500px;
    height:10px;
    background-color: black;
    border-radius: 5px;
}
.inner{
     
    position: relative;
    width: 0;
    height:10px;
    background-color: red;
    border-radius: 5px;
}
.innerBtn{
     
    position: absolute;
    width: 20px;
    height: 20px;
    top:-5px;
    right: -5px;
    border-radius: 50%;
    background: blue;
}

然后使用audio标签自带的方法和属性
this.audio.current.duration音频总时长
this.audio.current.currentTime当前播放到的时长
this.audio.current.playbackRate音频播放速度

this.audio.current.play();开始播放
this.audio.current.pause();暂停
this.audio.current.οncanplay=()=>{ } 当音频可以播放时执行
this.audio.current.οnwaiting=()=>{ } 当音频在缓冲时执行
this.audio.current.οntimeupdate=()=>{ } 当播放时间变动时(播放中)执行

监听audio的事件

 handleAudio() {
     
    let audio = this.audio.current;
    // timeupdate,播放位置改变时触发
    audio.addEventListener("timeupdate", () => {
     
      console.log("播放位置改变");
    });
    // canplay,表示音频可以播放了,准备就绪
    audio.addEventListener("canplay", () => {
     
      console.log("可以播放了");
    });
    // progress,音频在加载或缓冲
    audio.addEventListener("progress", () => {
     
      console.log("音频在加载或缓冲");
    });
    // waiting,需要缓冲下一帧而停止播放时触发
    audio.addEventListener("waiting", () => {
     
      console.log("需要缓冲下一帧");
    });
    // playing,音频或视频已开始播放时触发
    audio.addEventListener("playing", () => {
     
      console.log("开始播放");
    });
    // pause,音频或视频文件暂停时触发
    audio.addEventListener("pause", () => {
     
      console.log("已经暂停");
    });
  }

audio的duration和currentTime显示的是总秒数,需要我们自己转换成常规的时间格式。获取分钟的时间是总秒数除以60并对结果向下取整,获取秒数的时间是总秒数取余60并对结果向下取整

(Math.floor(this.audio.current.currentTime / 60) + "").padStart(2,"0")+":"+(Math.floor(this.audio.current.currentTime % 60) +"").padStart(2, "0");

padStart是字符串的补全方法,当位数不足两位数时就在字符串的开头补上第二个参数"0",之所以对结果加一个空的字符串也是运用"+“运算符有隐式类型转换(”+"只要有一侧是字符串,则两边都转换为字符串)将结果转换为字符串才能调用padStart

播放条进度更新

当前时长除以总时长乘100得到进度条的百分比,然后把这个结果给到内核的宽

this.inner.current.style.width =
(this.audio.current.currentTime / this.audio.current.duration) * 100 +"%";

点击进度条时跳到相应的播放时间

这要用到事件对象event,用鼠标到可视区左侧的距离减去父元素到可视区左侧的距离,这个距离就是内核的宽了

var innerWidth =e.targetTouches[0].clientX-this.outer.current.getBoundingClientRect().left;

内核的宽除以外框的宽,再用这个结果乘以总时长就得到了当前的播放时长

this.audio.current.currentTime=(innerWidth / outerWidth) * this.audio.current.duration;

至此整个自定义audio标签完成!
react+typescript使用ref实现自定义的audio标签_第1张图片

完成的项目代码:

import React, {
      Component } from "react";
import "./pack.css";
interface state {
     
  url: string;
  current: string;
  allTime: string;
}
interface Props {
     }

class Audio extends Component<any, state, Props> {
     
  outer: React.RefObject<any>;
  inner: React.RefObject<any>;
  audio: React.RefObject<any>;
  constructor(props: Props) {
     
    super(props);
    this.state = {
     
      url: "https://music.163.com/song/media/outer/url?id=1349292048.mp3", //歌曲地址
      current: "00:00", //当前时间
      allTime: "00:00", //当前歌曲时长
    };
    this.outer = React.createRef(); //自定义进度条外框
    this.inner = React.createRef(); //自定义进度条内核
    this.audio = React.createRef();
    this.audioX = this.audioX.bind(this);
    this.canplay = this.canplay.bind(this);
    this.timeUpdate = this.timeUpdate.bind(this);
    this.audioPlayBackRate = this.audioPlayBackRate.bind(this);
    this.audioPlay = this.audioPlay.bind(this);
    this.audioStop = this.audioStop.bind(this);
    this.audioGo = this.audioGo.bind(this);
    this.audioBack = this.audioBack.bind(this);
    this.audioWaiting = this.audioWaiting.bind(this);
    this.handleAudio = this.handleAudio.bind(this);
  }
  componentDidMount() {
     
    // console.log(this.audio);
    this.canplay();
    this.timeUpdate();
    this.handleAudio();
  }
  //在音频可以播放时就显示总时长
  canplay() {
     
    this.audio.current.oncanplay = () => {
     
      var allTime =
        (Math.floor(this.audio.current.duration / 60) + "").padStart(2, "0") +
        ":" +
        (Math.floor(this.audio.current.duration % 60) + "").padStart(2, "0");
      this.setState({
     
        allTime
      });
    };
  }
  audioWaiting() {
     
    this.audio.current.onwaiting = () => {
     
      alert("加载中");
    };
  }
  //在音频播放时当前时间也跟着变化
  timeUpdate() {
     
    this.audio.current.ontimeupdate = () => {
     
      // console.log(this.audio.current.currentTime)
      // console.log(this.audio.current.duration)
      var currentTime =
        (Math.floor(this.audio.current.currentTime / 60) + "").padStart(
          2,
          "0"
        ) +
        ":" +
        (Math.floor(this.audio.current.currentTime % 60) + "").padStart(2, "0");
      this.setState({
     
        current: currentTime
      });
      this.inner.current.style.width =
        (this.audio.current.currentTime / this.audio.current.duration) * 100 +
        "%";
    };
  }
  //播放速度
  audioPlayBackRate(str: string) {
     
    this.audio.current.playbackRate = str;
  }
  //播放开始
  audioPlay() {
     
    this.audio.current.play();
  }
  //播放暂停
  audioStop() {
     
    this.audio.current.pause();
  }
  //快进
  audioGo() {
     
    this.audio.current.currentTime += 20;
  }
  //快退
  audioBack() {
     
    this.audio.current.currentTime -= 20;
  }
  //设置播放进度
  audioX(e: any) {
     
    console.log(e.targetTouches[0].clientX);
    var outerWidth = this.outer.current.offsetWidth; //父元素宽
    var innerWidth =
      e.targetTouches[0].clientX -
      this.outer.current.getBoundingClientRect().left; //鼠标到可视区左侧的距离-父元素到可视区左侧的距离
    if (innerWidth > outerWidth || innerWidth < 0) {
     
      return;
    }
    this.inner.current.style.width = innerWidth + "px";
    this.audio.current.currentTime =
      (innerWidth / outerWidth) * this.audio.current.duration;
  }
  handleAudio() {
     
    let audio = this.audio.current;
    // timeupdate,播放位置改变时触发
    audio.addEventListener("timeupdate", () => {
     
      console.log("播放位置改变");
    });
    // canplay,表示音频可以播放了,准备就绪
    audio.addEventListener("canplay", () => {
     
      console.log("可以播放了");
    });
    // progress,音频在加载或缓冲
    audio.addEventListener("progress", () => {
     
      console.log("音频在加载或缓冲");
    });
    // waiting,需要缓冲下一帧而停止播放时触发
    audio.addEventListener("waiting", () => {
     
      console.log("需要缓冲下一帧");
    });
    // playing,音频或视频已开始播放时触发
    audio.addEventListener("playing", () => {
     
      console.log("开始播放");
    });
    // pause,音频或视频文件暂停时触发
    audio.addEventListener("pause", () => {
     
      console.log("已经暂停");
    });
  }
  render() {
     
    const {
      url, current, allTime } = this.state;
    return (
      <div>
        <h1>onTouchMove</h1>
        <h1>当前时间:{
     current}</h1>
        <h1>总时间:{
     allTime}</h1>
        <div
          className="outer"
          ref={
     this.outer}
          onTouchMove={
     e => this.audioX(e)}
          onTouchStart={
     e => this.audioX(e)}
        >
          <div className="inner" ref={
     this.inner}>
            <div className="innerBtn"></div>
          </div>
        </div>
        <br />
        <br />
        <br />
        <audio ref={
     this.audio} src={
     url} controls>
          你的浏览器不支持音频播放哦!(;´༎ຶД༎ຶ`)
        </audio>
        <button onClick={
     () => this.audioPlay()}>播放</button>
        <button onClick={
     () => this.audioStop()}>暂停</button>
        <button onClick={
     () => this.audioGo()}>快进</button>
        <button onClick={
     () => this.audioBack()}>快退</button>
        <button onClick={
     () => this.audioPlayBackRate("4.0")}>
          播放速度4.0
        </button>
      </div>
    );
  }
}

export default Audio;

你可能感兴趣的:(typescript,reactjs)