HTML

Video相关属性

1、自动播放

目前主流浏览器加强了对自动播放策略(Autoplay)的限制:浏览器在没有交互操作之前不允许有声音的媒体文件自动播放。 而且各个浏览器关于自动播放策略有不同的实现。

  1. {/* 自动播放 */}
  2. <video ref={ videoRef } controls autoPlay />

为了解决自动播放失败,在这里介绍两种方法解决 Autoplay 限制的方案

播放失败时绕过 Autoplay 限制
直接绕过 Autoplay 限制

1.1 播放失败时绕过 Autoplay 限制

在实际使用中,页面并不是完全被 Autoplay 限制,随着用户使用这个页面的次数增加,浏览器会将这个页面加入自己的 Autoplay 白名单列表中。
根据这个原理,可在检测到播放失败时,引导用户点击页面上的某个位置来恢复播放。(Google浏览器下测试均播放失败)

  1. // 可播放监听。当浏览器能够开始播放指定的音频/视频时触发
  2. this.videoRef.addEventListener('canplay', () => {
  3. console.log('视频可以播放了')
  4. setTimeout(() => {
  5. // this.videoRef.paused 判断是否暂停,用来判断是视频是否在播放中,如果没有播放就
  6. console.log(this.videoRef.paused)
  7. console.log('视频是否在暂停中', this.videoRef.paused)
  8. this.isPlay = !this.videoRef.paused
  9. }, 500)
  10. })
  11. // 通过promise来判断是否在播放
  12. const videoPromise = this.videoRef.play()
  13. if(!!videoPromise) {
  14. videoPromise.then(() => {
  15. this.isPlay = true
  16. console.log('播放成功')
  17. }).catch(() => {
  18. this.isPlay = false
  19. console.log('播放失败')
  20. })
  21. } else {
  22. // 此时可以通过canplay 监听是否在播放
  23. }

1.2 直接绕过 Autoplay 限制

可以通过如下两种方案实现直接绕过 Autoplay 限制
video标签中关闭静音muted属性设置为true。媒体不包含声音时不会被 Autoplay 限制。(引导用户开启声音)
UI上引导用户触发播放 :::tips 注意:无论使用哪种方案,在自动播放策略的限制下,没有用户操作之前都不可能自动播放有声媒体。 ::: 虽然浏览器会在本地维护一个白名单来决定对哪些网站解除自动播放限制,但该白名单无法通过 JavaScript 探测到。

  1. // 移动端
  2. document.body.addEventListener('touchstart', () => {
  3. console.log('触发播放')
  4. this.videoRef.play();
  5. })
  6. // PC端
  7. document.body.addEventListener('mousedown', () => {
  8. console.log('触发播放')
  9. this.videoRef.play();
  10. })
  11. // 微信端IOS手机下触发自动播放,大部分IOS能正常自动播放(安卓机只能通过touchstart触发播放)
  12. document.body.addEventListener('WeixinjsBridgeReady', () => {
  13. console.log('触发播放')
  14. this.videoRef.play();
  15. })

:::tips 注意: Safari 只允许通过用户交互来触发有声媒体的播放,而不是在用户交互后就打开 Autoplay 限制。 :::

2、播放时间属性控制

首先来看一段代码,在Google端能够正常播放,但是在移动端和Safari中还是重头开始播放。使用了canplayloadedmetadataoncanplay事件来判断视频状态再设置currentTime,但移动端还是无效。

  1. const videoPromise = this.videoRef.play()
  2. this.videoRef.currentTime = 10

解决方案:
设置视频的Timeupdate事件监听设置播放时间
使用定时器设置播放时间

  1. this.videoRef.play();
  2. // 通过时间更新播放时间
  3. let timer = setTimeout(function(){
  4. // 这里还是有一定的缺陷,如果用户触发了视频播放,但是加载比较长就会有问题
  5. this.videoRef.currentTime = 需要设置的时间;
  6. clearTimeout(timer);
  7. },200);
  8. // timeupdate:目前的播放位置已更改时,播放时间更新
  9. this.videoRef.addEventListener('timeupdate', (e) => {
  10. console.log('timeupdate')
  11. let timeDisplay = Math.floor(this.videoRef.currentTime);
  12. if(timeDisplay > 0){
  13. if(this.firstOpen){
  14. this.videoRef.currentTime = 10;
  15. this.firstOpen = false;
  16. }
  17. }
  18. })
  19. // seeking:查找开始。当用户开始移动/跳跃到音频/视频中新的位置时触发
  20. this.videoRef.addEventListener('seeking', (e) => {
  21. // 在这里处理视频播放是否播到放指定的时间
  22. console.log('开始移动进度条', this.videoRef.currentTime)
  23. })
  24. // seeked:查找结束。当用户已经移动/跳跃到视频中新的位置时触发
  25. this.videoRef.addEventListener('seeked', (e) => {
  26. console.log('进度条已经移动到了新的位置', this.videoRef.currentTime)
  27. })

其他属性介绍:

this.videoRef.error; //null:正常
this.videoRef.error.code; //1.用户终止 2.网络错误 3.解码错误 4.URL无效
this.videoRef.networkState; //0.此元素未初始化 1.正常但没有使用网络 2.正在下载数据 3.没有找到资源
this.videoRef.buffered; //返回已缓冲区域,TimeRanges
this.videoRef.paused; //是否暂停
this.videoRef.defaultPlaybackRate = value; //默认的回放速度,可以设置
this.videoRef.playbackRate = value; //当前播放速度,设置后马上改变
this.videoRef.played; //返回已经播放的区域,TimeRanges,
this.videoRef.seekable; //返回可以seek的区域 TimeRanges
this.videoRef.ended; //是否结束

Video相关事件

了解Video 标签相关事件触发时机,才能处理好业务相关逻辑。

  1. // loadstart 视频查找。当浏览器开始寻找指定的音频/视频时触发,也就是当加载过程开始时
  2. this.videoRef.addEventListener('loadstart', (e) => {
  3. console.log('提示视频的元数据已加载')
  4. // console.log(e)
  5. console.log(this.videoRef.duration) // NaN
  6. })
  7. // durationchange 时长变化。当指定的音频/视频的时长数据发生变化时触发,加载后,时长由 NaN 变为音频/视频的实际时长
  8. this.videoRef.addEventListener('durationchange', (e) => {
  9. console.log('提示视频的时长已改变')
  10. console.log(this.videoRef.duration) // 视频的实际时长(单位:秒)
  11. })
  12. // loadedmetadata :元数据加载。当指定的音频/视频的元数据已加载时触发,元数据包括:时长、尺寸(仅视频)以及文本轨道
  13. this.videoRef.addEventListener('loadedmetadata', (e) => {
  14. console.log('提示视频的元数据已加载')
  15. // console.log(e)
  16. })
  17. // loadeddata:视频下载监听。当当前帧的数据已加载,但没有足够的数据来播放指定音频/视频的下一帧时触发
  18. this.videoRef.addEventListener('loadeddata', (e) => {
  19. console.log('提示当前帧的数据是可用的')
  20. })
  21. // progress:浏览器下载监听。当浏览器正在下载指定的音频/视频时触发
  22. this.videoRef.addEventListener('progress', (e) => {
  23. console.log('提示视频正在下载中')
  24. })
  25. // canplay:可播放监听。当浏览器能够开始播放指定的音频/视频时触发
  26. this.videoRef.addEventListener('canplay', (e) => {
  27. console.log('视频可以播放了')
  28. setTimeout(() => {
  29. // this.videoRef.paused 判断是否暂停
  30. console.log('视频是否在暂停中', this.videoRef.paused)
  31. this.isPlay = !this.videoRef.paused
  32. }, 1000)
  33. })
  34. // canplaythrough:可流畅播放。当浏览器预计能够在不停下来进行缓冲的情况下持续播放指定的音频/视频时触发
  35. this.videoRef.addEventListener('canplaythrough', (e) => {
  36. console.log('提示视频能够不停顿地一直播放')
  37. console.log(e)
  38. })
  39. // play: 播放监听
  40. this.videoRef.addEventListener('play', (e) => {
  41. console.log('提示该视频正在播放中')
  42. })
  43. // pause:暂停监听
  44. this.videoRef.addEventListener('pause', (e) => {
  45. console.log('暂停播放')
  46. })
  47. // seeking:查找开始。当用户开始移动/跳跃到音频/视频中新的位置时触发
  48. this.videoRef.addEventListener('seeking', (e) => {
  49. // 在这里处理到底有没有更新到最新的位置
  50. console.log('开始移动进度条', this.videoRef.currentTime)
  51. })
  52. // seeked:查找结束。当用户已经移动/跳跃到视频中新的位置时触发
  53. this.videoRef.addEventListener('seeked', (e) => {
  54. console.log('进度条已经移动到了新的位置', this.videoRef.currentTime)
  55. })
  56. // waiting:视频加载等待。当视频由于需要缓冲下一帧而停止,等待时触发
  57. this.videoRef.addEventListener('waiting', (e) => {
  58. console.log('视频加载等待')
  59. console.log(e)
  60. })
  61. // playing:当视频在已因缓冲而暂停或停止后已就绪时触发
  62. this.videoRef.addEventListener('playing', (e) => {
  63. console.log('playing')
  64. console.log(e)
  65. })
  66. // timeupdate:目前的播放位置已更改时,播放时间更新
  67. this.videoRef.addEventListener('timeupdate', (e) => {
  68. // console.log('timeupdate')
  69. // let timeDisplay = Math.floor(this.videoRef.currentTime);
  70. // if(timeDisplay > 0){
  71. // if(this.firstOpen){
  72. // this.videoRef.currentTime = 10;
  73. // this.firstOpen = false;
  74. // }
  75. // }
  76. })
  77. // ended:播放结束
  78. this.videoRef.addEventListener('ended', (e) => {
  79. console.log('视频播放完了')
  80. console.log(e)
  81. })
  82. // error:播放错误
  83. this.videoRef.addEventListener('error', (e) => {
  84. console.log('视频出错了')
  85. console.log(e)
  86. })
  87. // volumechange:当音量更改时
  88. this.videoRef.addEventListener('volumechange', (e) => {
  89. console.log('volumechange')
  90. console.log(e)
  91. })
  92. // stalled:当浏览器尝试获取媒体数据,但数据不可用时
  93. this.videoRef.addEventListener('stalled', (e) => {
  94. console.log('stalled')
  95. console.log(e)
  96. })
  97. // ratechange:当视频的播放速度已更改时
  98. this.videoRef.addEventListener('ratechange', (e) => {
  99. console.log('ratechange')
  100. console.log(e)
  101. })

关于微信H5视频兼容介绍: 移动端兼容