HTML5中视频和音频核心事件讲解

译文
开发 前端
HTML 5中的视频和音频中有不少核心的事件,其中有的比较容易理解,基本能从字面就解析了,比如“play”事件就很好理解。而其他有的事件则需要花费点心思,

HTML 5中的视频和音频中有不少核心的事件,其中有的比较容易理解,基本能从字面就解析了,比如“play”事件就很好理解。而其他有的事件则需要花费点心思,特别是“progress”事件。因此,在本文中,将带领读者研究HTML 5视频和音频中重要的事件,探究这些事件是应该在什么时候使用以及其中的重要相关属性。我们也将看下这些事件在当今不同浏览器中的差异。为了本文的测试,使用的浏览器如下:Opera 12、Chrome 28、IE 10、Firefox 22、Safari 5(桌面版)和Mobile Safari 6(iOS版本)。

播放相关的事件

我们先来看下视频和音频中的playback事件。plackback事件发生的时机在播放媒体或停止媒体中,这个比较好理解,下面来仔细研究一下。其中“play”和“pause”事件分别在媒体播放和停止的时候触发,但也有一个“ended”事件,该事件是在媒体播放完毕到达***的时候触发-无论是正常情况下的结束或是用户自己动播放条到媒体文件的***。

这两个对应事件的触发很简单,只需要调用play()和pause()方法就可以了。它们也有对应的属性,其中.paused属性默认设置为true,而.ended属性默认是设置为false的,当然当媒体文件播放到***的时候,.ended属性则变为true。

然而,在Opera、Safari和IE 10中,请注意有个区别,就是即使媒体播放完毕,其中的.paused属性依然为false(从逻辑上说,媒体已经播放完毕了)。这样一个实际的结果是play/pause按钮的事件都不会触发了,看下面的例子:

  1. button.addEventListener('click', function(e) 
  2.   if(media.paused) 
  3.   { 
  4.     media.play(); 
  5.   } 
  6.   else 
  7.   { 
  8.     media.pause(); 
  9.   } 
  10. }, false); 

也就是说,当视频结束后,其pause()事件依然能调用。我们可以通过一个小技巧去修正这个问题,如下代码所示:

  1. media.addEventListener('ended', function(e)  
  2.  
  3. {  
  4.  
  5.   media.pause();  
  6.  
  7. }, false);  

也就是在监听ended事件中,主动调用media.pause()方法就可以了。在Firefox和Chrome的***版本中,已经修正了这个问题。

关于加载事件

加载事件总是在媒体加载(或加载失败)的时候发生。加载事件受到加载媒体状态的影响,比如是否使用了preload属性又或者是媒体是否缓存了。下面我们来逐一分析下其中关键的事件,首先是“loadstart”事件,含义是让浏览器刚开始的时候去寻找获得数据。但要注意的是,loadstart并不意味着就任何数据就会马上加载,还要看preload属性的值的设置。如果preload(预装载的值)设置为“none”的话,则“loadstart”事件就是唯一在播放视频前触发的事件。如果preload属性设置为“metadata”或者是“auto”,则会触发“progress”和“loadmetadata”事件(如果没预加载的话,这两个事件也会触发,但不会在播放前发生)。

在下一节中,我们才学习“progress”事件,由于其比较复杂。我们先来学习比较简单的“loadedmetadata”事件。正如字面的含义,浏览器仅仅加载媒体的元数据信息而已,比如其长度.duration(是一个浮点数而不是默认的NaN)。

当然,“loadedmetadata”事件只有在确认能加载媒体文件后才能加载,换句话说,如果某个媒体问题文件不能加载(比如404错误),则会直接产生error事件,当然也不可能继续运行相关的播放事件了。

这里,又要提醒用户有的浏览器中是有差别的。在Mobile Safari中,preload的属性其实是没声明的,就等于设置为“none”一样了。但在IE 10中,又有不同了,其中媒体的元数据默认是自动加载的,所以preload设置为none其实跟设置为metadata的作用是一样的。

在“loadedmetadata”事件触发后,接下来的重要事件就是“canplay”,这个是浏览器用来确认是否已经装载足够的媒体信息到浏览器中能播放一个事件。如果preload属性设置为“auto”,则在数据装载后大概几秒,“canplay”事件就会触发;如果preload设置为“metadata”或者是“none”,则直到播放开始时才会触发。这个规则对Chrome浏览器来说是例外的,在Chrome中,即使只是加载元数据阶段,“canplay”事件也会触发。

同时也有仅接着的事件叫“canplaythrough”,这个事件其实是给浏览器根据当前网络状况去判断是否已经加载了足够的媒体片断而支撑基本的播放。所以这个事件要在数据被预加载大概几秒后才会触发。

在实际情况下,“canplaythrough”事件基本是没啥作用的,因为Safari根本不会触发这个事件,而Opera和Chrome则在“canplay”事件触发后马上就触发这个事件,只有FireFox和IE 10实现了这个事件。

所以实际上,开发者最应该关心的是监视“progress”事件,以了解媒体到底加载了多少(必要的时候可以计算媒体的下载速度)。

Progress事件

接下来我们重点学习下progress事件。该事件在数据正在下载的时候会触发。所以当preload设置为none的时候,progress事件在知道播放事件真正开始前是不会触发的。如果preload设置为“metadata”,则该事件会短暂触发大概几秒,然后停止,直到真正的播放行为开始时才触发;如果preload设置为“auto”,则会触发一直直到整个媒体文件下载完毕。

无论preload如何设置,一旦用户开始进行播放的行为,则浏览器会开始下载整个媒体文件,则会持续触发progress事件,一直直到整个文件下载完毕,即使视频被暂停。

当数据下载后,则我们需要了解如何用时间表达这个progress的事件,则对接下来深入了解progress事件是十分重要的。当数据开始加载时,会创建表示媒体播放时间的范围,比如一旦头10秒的数据已经加载,则以数组的方式记录了开始和结束时间,如下的方式表示:

  1. [0,10] 

当然,实际上是会有多个时间范围存在的,比如用户手工使用播放器的进度条去移动到想要的位置,则浏览器会忽略当前的时间范围而加载新的部分而不是象 Flash那样重新加载两个时间点之间的部分。

比如我们加载10秒的视频后,跳到两分钟后的片断继续播放另外的10秒,则有两个时间范围,用如下的方式表达:

  1.   [0,10], 
  2.   [120,130] 

假设用户这个时候又回看旧的片断,则继续又增加一个时间范围的数组,如:

  1.   [0,10], 
  2.   [60,70], 
  3.   [120,130] 

如果从60秒开始看到130秒结束,则***的时间范围合拼为:

  1.   [0,10], 
  2.   [60,130] 

上面的例子只是简单说明在播放媒体中,时间的范围是如何运作的,当然实际上的数据保存不是这个样子。实际上媒体都有一个.buffered对象以表示时间范围。.buffered对象有一个.length长度属性表示有多少段时间范围,并且有对应的start()和end()方法,所以我们可以将buffered的数据转换为二维数组,如下代码所示:

  1. var ranges = []; 
  2. for(var i = 0; i < media.buffered.length; i ++) 
  3.   ranges.push([ 
  4.     media.buffered.start(i), 
  5.     media.buffered.end(i) 
  6.     ]); 

***,我们可以自定义progress事件如下:

  1. media.addEventListener('progress', function() 
  2.   var ranges = []; 
  3.   for(var i = 0; i < media.buffered.length; i ++) 
  4.   { 
  5.     ranges.push([ 
  6.       media.buffered.start(i), 
  7.       media.buffered.end(i) 
  8.       ]); 
  9.   } 
  10. }, false); 

有了这个方法,则我们可以针对progress事件进行一些定制开发。比如我们可以实现一个简单的播放视频,并且提供一个额外的进度条,在视频加载过程中能看到其进度。其实际的运行效果请参考:http://jspro.brothercake.com/media-events/progress.html ,下面是一个运行效果截图:

下面选取其中的代码片断讲解其核心原理:

  1. function onprogress() 
  2.         { 
  3.             //获得buffered数据 
  4.             var ranges = []; 
  5.             for(var i = 0; i < media.buffered.length; i ++) 
  6.             { 
  7.                 ranges.push([ 
  8.                     media.buffered.start(i), 
  9.                     media.buffered.end(i) 
  10.                     ]); 
  11.             } 
  12.              
  13.             //获得在容器中的当前进度 
  14.             var spans = progress.getElementsByTagName('span'); 
  15.              
  16.             //如果还没有加载完毕则继续加载 
  17.             while(spans.length < media.buffered.length
  18.             { 
  19.                 progress.appendChild(document.createElement('span')); 
  20.             } 
  21.             while(spans.length > media.buffered.length) 
  22.             { 
  23.                 progress.removeChild(progress.lastChild); 
  24.             } 
  25.                  
  26.              
  27.             for(var i = 0; i < media.buffered.length; i ++) 
  28.             { 
  29.                 spans[i].style.left = Math.round 
  30.                 ( 
  31.                     (100 / media.duration)                  *  
  32.                     ranges[i][0] 
  33.                 )  
  34.                 + '%'; 
  35.                  
  36.                 spans[i].style.width = Math.round 
  37.                 ( 
  38.                     (100 / media.duration)                  *  
  39.                     (ranges[i][1] - ranges[i][0]) 
  40.                 )  
  41.                 + '%'; 
  42.             } 
  43.         } 

在上面的代码段中,使用的是设置<span>的宽度去代表进度条的每一个格的宽度,首先获得buffered数据,存放到数据ranges中,然后判断媒体文件是否加载完毕,如果还没加载完毕,则继续往DOM中添加<span>标签,而这个span标签的宽度和样式则是通过上面的代码按百分比计算出来。

 

***,用户可以通过http://jspro.brothercake.com/media-events/events.html的例子,学习到本文中提到的在媒体加载前、加载中和加载后浏览器的相关事件,在这个例子中会输出相关的日志。

 

责任编辑:陈四芳 来源: 51CTO
相关推荐

2017-08-09 15:57:11

JavaScriptHtml5音频

2012-12-07 10:20:56

IBMdW

2012-04-27 14:34:15

2012-06-04 13:44:08

2012-03-07 15:24:41

2013-01-30 15:35:47

AdobeHTML5

2014-12-30 17:13:51

HTML5

2011-03-11 08:38:17

HTML 5ASP.NET 4

2015-10-08 16:02:28

HTML5Video

2013-01-24 10:26:04

HTML5HTML 5HTML5的未来

2010-01-14 09:39:47

HTML 5audiovideo

2013-05-22 09:59:10

HTML 5音频

2013-10-21 15:24:49

html5游戏

2011-05-13 17:36:05

HTML

2014-11-26 10:14:10

AndroidiOSHTML5

2011-05-13 17:41:40

2011-01-14 17:53:33

HTML5cssweb

2023-03-16 09:00:00

HTML5HTML语言

2023-02-08 08:11:58

Spring容器核心事件

2017-01-03 18:09:33

HTML5本地存储Web
点赞
收藏

51CTO技术栈公众号