2017-10-04 3 views
2

J'ai un composant React.js, qui est un fichier audio et un composant enfant, où est un curseur avec bouton lecture/pause. Je veux que la position de la flèche dans le curseur soit synchronisée avec l'heure actuelle.React.js synchronisation des composants avec le support html5 temps actuel

Lors de la lecture d'un fichier audio, l'animation de la flèche est retardée, parfois plus et parfois moins. Lorsque l'audio est en pause, l'animation de la flèche se produit immédiatement. Comment puis-je obtenir le même effet lorsque l'audio est joué sans délai?

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

est 'this.props.currentTime' en secondes ou millisecondes? – bennygenel

+0

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

Répondre

2

Je pense que parce que le son $.animate n'est pas terminée lorsque la nouvelle animation a commencé. Selon jQuery docsslow la durée de l'animation est de 600ms.

Il n'est pas recommandé de combiner jQuery et de réagir. Pour cette raison, je vous suggère d'utiliser CSS transitions pour obtenir le comportement souhaité.

Exemple

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