2017-10-04 76 views
2

我有一個React.js組件,它是一個audio文件和一個子組件,其中帶有播放/暫停按鈕的滑塊。我希望滑塊中的箭頭位置與音頻當前時間同步。React.js組件與html5媒體同步當前時間

播放音頻時,箭頭動畫會延遲,有時會更多,有時甚至更少。當音頻暫停時,箭頭動畫會立即出現。播放音頻時如何才能達到相同的效果?

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

是'this.props.currentTime'在秒或毫秒? – bennygenel

+0

@bennygenel [seconds](https://www.w3schools.com/tags/av_prop_currenttime.asp) – Matt

回答

2

我的猜測是,它因爲$.animate沒有被當新的動畫開始結束。根據jQuery docsslow動畫持續時間是600ms。

這是不是一個好的做法,結合jQuery和反應。因此,我建議您使用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> 
    ); 
    }