You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-howler在不同浏览器中音频播放速度不一致问题求助

跨浏览器React Howler音频时序兼容性问题求助

我在使用React Howler播放器从指定位置加载音频时遇到以下跨浏览器兼容性问题:

  • 播放约20分钟后,Chrome中的音频与Firefox、Safari不同步
  • 即使时间戳准确,Chrome的音频起始位置也与Firefox、Safari不一致,拖动进度条时同样存在该问题

我使用的是React-howler全控制代码,代码如下:

class Player extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      playing: false,
      preload: false,
      loaded: false,
      loop: false,
      mute: false,
      volume: 1.0,
      seek: 0,
      rate: 1,
      isSeeking: false
    }
    this.handleToggle = this.handleToggle.bind(this)
    this.handleOnLoad = this.handleOnLoad.bind(this)
    this.handleOnEnd = this.handleOnEnd.bind(this)
    this.handleOnPlay = this.handleOnPlay.bind(this)
    this.handleStop = this.handleStop.bind(this)
    this.renderSeekPos = this.renderSeekPos.bind(this)
    this.handleLoopToggle = this.handleLoopToggle.bind(this)
    this.handleMuteToggle = this.handleMuteToggle.bind(this)
    this.handleMouseDownSeek = this.handleMouseDownSeek.bind(this)
    this.handleMouseUpSeek = this.handleMouseUpSeek.bind(this)
    this.handleSeekingChange = this.handleSeekingChange.bind(this)
    this.handleRate = this.handleRate.bind(this)
    this.resetRecording = this.resetRecording.bind(this)
  }
  componentWillUnmount () {
    this.clearRAF()
  }

  handleToggle () {
    this.setState({
      playing: !this.state.playing
    })
  }

  resetRecording () {
    this.setState({
      playing: false
    })
    this.player.seek(this.props.startTime)
    this.renderSeekPos()
  }

  handleOnLoad () {
    this.setState({
      loaded: true,
      duration: this.player.duration()
    })
    this.player.seek(this.props.startTime)
  }

  handleOnPlay () {
    this.setState({
      playing: true
    })
    this.renderSeekPos()
  }

  handleOnEnd () {
    this.setState({
      playing: false
    })
    this.clearRAF()
  }

  handleStop () {
    this.player.stop()
    this.setState({
      playing: false // Need to update our local state so we don't immediately invoke autoplay
    })
    this.renderSeekPos()
  }

  handleLoopToggle () {
    this.setState({
      loop: !this.state.loop
    })
  }

  handleMuteToggle () {
    this.setState({
      mute: !this.state.mute
    })
  }

  handleMouseDownSeek () {
    this.setState({
      playing: false,
      isSeeking: true
    })
  }

  handleMouseUpSeek (e) {
    this.setState({
      isSeeking: false
    })

    this.player.seek(e.target.value)
  }

  handleSeekingChange (e) {
    this.setState({
      seek: parseFloat(e.target.value)
    })
  }

  renderSeekPos () {
    if (!this.state.isSeeking) {
      this.setState({
        seek: this.player.seek()
      })
    }
    if (this.state.playing) {
      this._raf = raf(this.renderSeekPos)
    }
  }

  handleRate (e) {
    const rate = parseFloat(e.target.value)
    this.player.rate(rate)
    this.setState({ rate })
  }

  clearRAF () {
    raf.cancel(this._raf)
  }

  render () {
    return (
      <div className='full-control'>
        <ReactHowler
          src={[this.props.recording]}
          preload={this.state.preload}
          playing={this.state.playing}
          onLoad={this.handleOnLoad}
          onPlay={this.handleOnPlay}
          onEnd={this.handleOnEnd}
          loop={this.state.loop}
          mute={this.state.mute}
          volume={this.state.volume}
          seek={this.state.seek}
          rate={this.state.rate}
          ref={(ref) => (this.player = ref)}
        />

        {this.state.loaded &&
        <div>
          <Button onClick={this.handleToggle}>
            {(this.state.playing) ? <FaPause /> : <FaPlay />}
          </Button>
          <Button onClick={this.resetRecording}>
            <BiReset/>
          </Button>
        </div>
        }

        {!this.state.loaded &&
        <Button onClick={this.handleToggle}>
          {(this.state.playing) ? <BiLoader /> : <FaPlay />}
        </Button>
        }

        {this.state.loaded &&
        <p className="timeline">
          {new Date(this.state.seek.toFixed(0) * 1000).toISOString().substr(11, 8)}
          {' / '}
          {(this.state.duration) ? new Date(this.state.duration.toFixed(0) * 1000).toISOString().substr(11, 8) : 'NaN'}
        </p>
        }

        <div className='seek'>
          <span className='slider-container'>
            <input
              type='range'
              min='0'
              max={this.state.duration ? this.state.duration.toFixed(2) : 0}
              step='.01'
              value={this.state.seek}
              onChange={this.handleSeekingChange}
              onMouseDown={this.handleMouseDownSeek}
              onMouseUp={this.handleMouseUpSeek}
            />
          </span>
        </div>
      </div>
    )
  }
}

export default Player

恳请有跨浏览器音频时序问题解决经验的开发者提供帮助。

内容的提问来源于stack exchange,提问作者Lea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:54:14