React.js组件与html5媒体当前时间同步

时间:2017-10-04 09:45:27

标签: javascript html5 reactjs audio

我有一个React.js组件,它是一个audio文件和一个子组件,其中是一个带有播放/暂停按钮的滑块。我希望滑块中的箭头位置与音频当前时间同步。

播放音频时,箭头动画会延迟,有时会更多,有时会更少。当音频暂停时,箭头动画立即发生。当音频播放没有延迟时,如何才能达到同样的效果?

jsfiddle

  class Player extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
    };

    this.playClick = this.playClick.bind(this);
    this.changePosition = this.changePosition.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    const that = this;
    if (this.props.currentTime !== nextProps.currentTime) {
        const horizontalOffset = nextProps.currentTime * 500 / this.props.duration;
      console.log('horOffset', horizontalOffset);
      $('.arrow').animate(
        { left: `${horizontalOffset - 20}px` },
        {
          duration: 'slow',
          easing: 'easeInOutExpo',
        },
      );
    }
  }

  changePosition(e){
    const newCurrTime = this.props.duration * e.clientX / 500;
    console.log('changed time', newCurrTime);
    this.props.onChangeAudioTime(newCurrTime);
  }

  playClick() {
    this.props.onTogglePlay();
  }

  render() {
    return <div>
    <button onClick={this.playClick}>play</button>
    <div className="slider-content" onClick={this.changePosition}>
      <div className="arrow">
      </div>
    </div>
    </div>;
  }
}

class Audio extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      play: false,
      current: 0,
      duration: 2024.496,
      seeked: false
    };

    this.toggle = this.toggle.bind(this);
    this.play = this.play.bind(this);
    this.pause = this.pause.bind(this);
    this.setCurrTime = this.setCurrTime.bind(this);
  }

  componentDidMount() {
    const that = this;
    this.currentTimeInterval = null;

    this.audio.onplay = () => {
      this.currentTimeInterval = setInterval(() => {
        if (that.state.current !== this.audio.currentTime) {
          that.setState({ current: this.audio.currentTime, seeked: false });
        }
      }, 500);
    };

    this.audio.onseeked = () => {
      this.currentTimeInterval = setInterval(() => {
        if (that.state.current !== this.audio.currentTime) {
          that.setState({ current: this.audio.currentTime, seeked: false });
        }
      }, 500);
    };

    this.audio.onpause = () => {
      clearInterval(this.currentTimeInterval);
    };
  }

  play() {
    this.setState({ play: true }, () => {
      this.audio.play();
    });
  }

  pause() {
    this.setState({ play: false }, () => {
      this.audio.pause();
    });
  }

  toggle() {
    this.state.play ? this.pause() : this.play();
  }

  setCurrTime(newCurrTime) {
    let that = this;
    clearInterval(this.currentTimeInterval);
    this.setState({ current: newCurrTime, seeked: true }, () => {
      this.audio.currentTime = newCurrTime;
    });
  }

  render() {
    return <div>
    <audio src="https://dts.podtrac.com/redirect.mp3/www.kqed.org/.stream/mp3splice/radio/theleap/2017/04/HennyMastered.mp3"
          id="audio"
          ref={el => (this.audio = el)}
        />
     <Player
            currentTime={this.state.current}
            duration={this.state.duration}
            onTogglePlay={this.toggle}
            onChangeAudioTime={this.setCurrTime}
            play={this.state.play}
            ref={el => (this.player = el)}
          />
    </div>;
  }
}

ReactDOM.render(
  <Audio name="World" />,
  document.getElementById('container')
);

1 个答案:

答案 0 :(得分:2)

我的猜测是因为$.animate在新动画开始时没有完成。根据{{​​3}} slow动画的持续时间为600毫秒。

将jQuery和反应结合起来并不是一个好习惯。因此,我建议您使用jQuery docs来实现所需的行为。

示例

  // in css file
  div.arrow {
    left: 0; /* desired initial left value */
    transition: left 500ms;
    -moz-transition: left 500ms; 
    -webkit-transition: left 500ms; 
    -o-transition: left 500ms;
  }

  // in component class
  componentWillReceiveProps(nextProps) {
    const that = this;
    if (this.props.currentTime !== nextProps.currentTime) {
        const horizontalOffset = nextProps.currentTime * 500 / this.props.duration;
      console.log('horOffset', horizontalOffset);
      this.setState({ leftValue: (horizontalOffset - 20) });
    }
  }

  render() {
    return (
      <div>
        <button onClick={this.playClick}>play</button>
        <div className="slider-content" onClick={this.changePosition}>
          <div className="arrow" style={{left: this.state.leftValue}}></div>
        </div>
      </div>
    );
  }