video频繁切换播放/暂停状态报错处理

1,525 阅读2分钟
前言

项目遇到一个需求:

  • 实现一个包含四层元素的动画按钮,这篇文章只单说明video这层的元素。
  • 需要控制其播放/暂停,鼠标移入使其播放(执行动画),鼠标移出则暂停。
问题

Uncaught (in promise): The play() request was interrupted by a call to pause().

<video
  onMouseEnter={play}
  onMouseLeave={pause}
>
  <source src={videoUrl} type="video/mp4" />
</video>
const play = () => {
 videoTarget.play();
}

const pause = () => {
 videoTarget.pause();
}
错误思路
  1. 使用setTimeout延迟执行,这样解决会延迟执行。有两个弊端,一是设置50ms、100ms甚至200ms也会触发相同的error,并且设置大了会明显感觉到延迟执行,体验不好。

      <video
        onMouseEnter={play}
        onMouseLeave={pause}
      >
        <source src={videoUrl} type="video/mp4" />
      </video>
    
    const play = () => {
      setTimeout(() => {
        videoTarget.play();
      }, 100)
    }
    
    const pause = () => {
      setTimeout(() => {
        videoTarget.pause();
      }, 100)
    }
    
  2. 使用play、pause方法返回的promise + isPlaying处理。这种处理方式,会导致isPlaying的设置并不是准确的,如果鼠标移动过快,或者多次尝试,也是有几率将isPlaying的值设置错的,故弃用。

    <video
      onMouseEnter={play}
      onMouseLeave={pause}
    >
      <source src={videoUrl} type="video/mp4" />
    </video>
    
    let isPlaying = false; // 初始状态
    
    const play = () => {
      if (isPlaying) {
        return;
      }
    
      videoTarget.play().then(() => {
        isPlaying = true;
      });
    }
    
    const pause = () => {
      if (!isPlaying) {
        return;
      }
    
      videoTarget.pause().then(() => {
        isPlaying = false;
      });
    }
    
解决方法
<video
  onMouseEnter={play}
  onMouseLeave={pause}
  onPlaying={playing}
  onPause={pausing}
>
  <source src={videoUrl} type="video/mp4" />
</video>
let isPlaying = false;
  
const playing = () => {
  isPlaying = true;
}

const pausing = () => {
  isPlaying = false;
}

const play = () => {
  if (videoTarget.paused && !isPlaying) {
    videoTarget.play();
  }
}

const pause = () => {
  if (!videoTarget.paused && isPlaying) {
    videoTarget.pause();
  }
}
分析

先了解这两个事件。

  • playing事件,当播放准备开始时(之前被暂停或者由于数据缺乏被暂缓)被触发
  • onpause可以用来获取或设置当前元素的onpause事件的事件处理函数。当媒体播放被暂停时,将触发pause事件。

video、audio这类媒体播放器在播放时,需要注意的点其实挺多的,这里单就播放/暂停这点,因为牵涉到硬件层,所以行为一定会有延迟,具体耗时其实是不太好确定的,所以这里是通过原生的回调事件去控制isPlaying这个flag,而不是promise去处理,promise处理方式会导致判断有误差。

我理解play或者pause方法返回的promise中再去处理的话,是延后了,这时修改flag,会影响判断,且onMouseEnter、onMouseLeave在鼠标一次移入、移出的过程中分别只会触发一次,所以isPlaying的修改要置前,在video行为准备过程中去处理,然后再切换播放/暂停,这样就不会让chrome报错,误认当前的播放状态相互影响。

结束语

如果觉得写的不错,有用的话,还请帮我点个赞 O(∩_∩)O
转发请注明出处,谢谢!