【问题标题】:React setInterval with update state makes onClick not working使用更新状态反应 setInterval 使 onClick 不起作用
【发布时间】:2018-12-22 14:30:03
【问题描述】:

我在 React 中制作了一个自定义音频播放器,它使用基于播放器持续时间的值更新音频文件(进度条)的所谓“Scrubber”。一切正常,除了如果您在间隔更新时按下播放/暂停按钮(mousedown -> 间隔更新状态 -> 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?

【问题讨论】:

  • 你能展示更多你的代码吗?我认为很难说当前问题中的代码有什么问题。
  • @Tholle 我真的不认为这是代码本身。更多的状态更新/onClick 事件,但我可以在更多部分进行编辑。给我一点时间。
  • 不。它甚至没有记录它。或者更清楚一点 - 如果我在间隔更新之前单击(mousedown 和 mouseup),它会按预期工作。然后它会像预期的那样记录和更新。而且我越来越困惑,因为 seek 方法可以正常工作。感谢您抽出宝贵时间提供帮助。
  • 例如,如果我将时间间隔更改为 10 秒,则点击会按我的意愿工作。但是,如果我单击并按住,等待 10 秒然后松开鼠标,则什么也没有发生。所以这肯定与更新间隔有关:D
  • 哈哈哈@Tholle,既然你投入了这个,我终于想通了!我用于播放按钮的图标以某种方式冒泡,但是当状态更新时,冒泡以某种方式消失了。我简直不敢相信。现在一切都按预期工作。感谢您的宝贵时间。

标签: javascript reactjs onclick setinterval html5-audio


【解决方案1】:

所以对于任何想知道在状态更新时单击图标没有正确冒泡的人来说。我所要做的就是在图标上添加pointer-events: none;,这样点击就会直接在 div 上而不是在图标上触发。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-15
    • 2020-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-29
    • 2021-07-27
    • 1970-01-01
    相关资源
    最近更新 更多