使用更新状态来反应setInterval会使onClick无法正常运行

时间:2018-07-14 11:43:04

标签: javascript reactjs onclick setinterval html5-audio

我在React中创建了一个自定义音频播放器,该音频播放器使用基于播放器持续时间的值来更新音频文件(进度条)的所谓“清理程序”。一切正常,除了如果在间隔更新时按下播放/暂停按钮(mousedown-> interval updates state-> mouseup),则单击按钮不会触发。

import React, { Component } from 'react';
import ReactSVG from 'react-svg';

import VolumeButton from 'components/VolumeButton/VolumeButton.jsx';
import play from 'img/icons/play.svg';
import pause from 'img/icons/pause.svg';
import './AudioPlayer.css';

class AudioPlayer extends Component {
    constructor(props) {
        super(props);
        this.state = {
            currentTime: '00:00',
            endTime: '00:00',
            intervalsInitiated: false,
            player: null,
            playing: false,
            progressBar: null
        };
        this.handleResize = this.handleResize.bind(this);
    }

    initIntervals = () => {
        let intervals = setInterval(() => {
            this.updateScrubber(this.state.player.currentTime);
            this.updateTime(this.state.player.currentTime);
        }, 100);
        this.setState({
            intervals: intervals,
            intervalsInitiated: true
        });
    };

    setUpAudioTime = player => {
        console.log('setup time');
        let playerCurrentTime = player.currentTime;

        let currentTime = this.calculateTime(playerCurrentTime);
        this.setState({
            currentTime: currentTime,
            endTime: this.calculateTime(player.duration)
        });
        this.updateScrubber(playerCurrentTime);
    };

    initPlayer = () => {
        console.log('initPlayer');

        // Add resize listener for showing volume on desktop
        this.addResize();
        this.handleResize();

        // Setup correct player
        let player = new Audio(this.props.audioPath);
        let progressBar = document.querySelector('.ProgressBar');

        // Add event for audio stopped for play button
        player.addEventListener('ended', () => this.toggleAudioPlay());

        // Set state to correct player
        // TODO: Do this in update aswell going from one to another
        this.setState({
            player: player,
            progressBar: progressBar
        });
        player.addEventListener('loadedmetadata', () =>
            this.setUpAudioTime(player)
        );
    };

    toggleAudioPlay = () => {
        console.log('toggling audio');
        if (!this.state.player) {
            return;
        }
        !this.state.intervalsInitiated
            ? this.initIntervals()
            : this.resetComponent();
        this.state.playing
            ? this.state.player.pause()
            : this.state.player.play();
        this.setState({
            playing: !this.state.playing
        });
    };

    updateScrubber = playerCurrentTime => {
        if (!this.state.player || !this.state.progressBar) {
            return;
        }
        let prog = this.state.progressBar;
        prog.value = playerCurrentTime / this.state.player.duration;
        this.setState({
            progressBar: prog
        });
    };

    updateTime = playerCurrentTime => {
        this.setState({
            currentTime: this.calculateTime(playerCurrentTime)
        });
    };

    calculateTime = lengthInSeconds => {
        let minutes = Math.floor(lengthInSeconds / 60);
        let seconds = Math.floor(lengthInSeconds) - minutes * 60;
        let time =
            (minutes < 10 ? '0' + minutes : minutes) +
            ':' +
            (seconds < 10 ? '0' + seconds : seconds);
        return time;
    };

    seek = event => {
        if (!this.state.player || !this.state.progressBar) {
            return;
        }
        let percent =
            event.nativeEvent.offsetX / this.state.progressBar.offsetWidth;
        let prog = this.state.progressBar;
        prog.value = percent;
        this.setState({ progressBar: prog });
        let newTime = percent * this.state.player.duration;
        let player = this.state.player;
        player.currentTime = newTime;
        this.setState({
            player: player
        });
        this.updateTime(newTime);
    };

    resetComponent() {
        console.log('reset component');
        clearInterval(this.state.intervals);
        this.setState({
            intervalsInitiated: false
        });
    }

    componentDidMount() {
        this.initPlayer();
    }

    render() {
        return (
            <div
                className={`AudioPlayer ${
                    this.state.width < 800 ? 'AudioPlayer--extended' : ''
                }`}
            >
                <audio className="MarkerAudio" preload="metadata">
                    <source src={this.props.audioPath} type="audio/mp3" />
                    Your device doesnt support audio format.
                </audio>
                <div className="PlayerControls">
                    <div className="PlayButton" onClick={this.toggleAudioPlay}>
                        {this.state.playing ? (
                            <ReactSVG
                                className="AudioIcon"
                                svgStyle={{ height: '100%', width: '100%' }}
                                path={pause}
                            />
                        ) : (
                            <ReactSVG
                                className="AudioIcon"
                                svgStyle={{
                                    height: '100%',
                                    left: '2px',
                                    position: 'relative',
                                    width: '100%'
                                }}
                                path={play}
                            />
                        )}
                    </div>
                    <span className="CurrentTime">
                        {this.state.currentTime}
                    </span>
                    <progress
                        onClick={this.seek}
                        className="ProgressBar"
                        value="0"
                        min="0"
                        max="1"
                    />
                    <span>{this.state.endTime}</span>
                </div>
            </div>
        );
    }
}

export default AudioPlayer;

但是,如果我确定要进行“快速单击”或未启动间隔,则单击仍然有效。关于如何处理的任何想法?我应该使用onMouseDown还是其他方式?还是有办法确保间隔和状态更改不会干扰onClick?

1 个答案:

答案 0 :(得分:0)

所以对于任何想知道它有什么作用的人,在状态更新时单击该图标都无法正常冒泡。为了解决这个问题,我要做的就是在图标上添加pointer-events: none;,以便直接在div而不是图标上触发点击。