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

标签 javascript html reactjs audio

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

播放音频时,箭头动画会延迟,有时多有时少。当音频暂停时,箭头动画立即出现。如何在音频无延迟播放的情况下达到同样的效果?

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')
);

最佳答案

我的猜测是,这是因为新动画开始时 $.animate 尚未完成。根据jQuery docs slow 动画持续时间为 600 毫秒。

将 jQuery 和 React 结合起来并不是一个好的做法。因此,我建议您使用 CSS transitions达到预期的行为。

示例

  // 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>
    );
  }

关于javascript - React.js组件与html5媒体当前时间同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46561781/

相关文章:

JavaScript:将输入元素添加到当前 div

javascript - 每次点击重复 div Angular

c# - 如何选择 GridView 中某一特定列中的所有复选框?

html - 填充以便顶部标题栏不会阻塞文本

php - 在 else 中创建错误

javascript - ReactJS View 处理具有不同数据的同一组件

javascript - 如果 Redux 异步 thunk 操作包含 promise ,那么它们是否应该始终返回 promise

javascript - 从父级 Angular 数据创建可编辑的子级

css - 限制 div 中的垂直内容

reactjs - 在没有回调函数的输入类型中设置 State