<video>
和<audio>
元素提供了广泛的事件范围。 尽管有些很简单,例如不言自明的"play"
事件,但有些可能很难理解,特别是"progress"
事件。
因此,让我们研究一些最重要的媒体事件,看看它们何时以及如何触发以及与它们相关的属性。 我们还将尝试在当前浏览器中导航其行为的怪癖(嗯,您不认为它们都一样,对吗?)。
(对于参考测试,我将使用最常见的浏览器的最新公共版本-Opera 12,Chrome 28,IE10,Firefox 22,Safari 5(台式机)和Mobile Safari 6(iOS)。因此,无论在何处引用浏览器仅以名称(例如Opera
)表示最新版本。)
播放事件
播放事件是响应播放或暂停媒体而触发的事件。 这些事件非常简单。
"play"
和"pause"
事件分别在播放或暂停媒体时触发,但是还有一个"ended"
事件,当媒体播放到末尾时触发-原因是普通播放已完成,或者是用户手动“寻求”了那么远。
有与前两个事件相对应的媒体函数-毫不奇怪地称为play()
和pause()
。 还有两个与最后两个事件对应的媒体属性.paused
属性默认为true
,或者每当媒体暂停时, .ended
属性默认为false
,但在播放结束时变为true
(即与"ended"
事件同时触发)。
但是,Opera,Safari和IE10中存在一个明显的异常,那就是当媒体结束时.paused
标志仍然为 false
( .paused
,由于媒体不再播放,因此应该为true
)。 一个实际的结果是,在这种情况下,像这样的简单播放/暂停按钮处理程序将失败(即按钮根本不起作用):
button.addEventListener('click', function(e)
{
if(media.paused)
{
media.play();
}
else
{
media.pause();
}
}, false);
但是您可以很容易地解决此问题,方法是通过手动触发pause()
方法来响应"ended"
事件:
media.addEventListener('ended', function(e)
{
media.pause();
}, false);
Firefox和Chrome已经在内部以完全相同的方式解决此问题,方法是在"ended"
事件之前触发"pause"
"ended"
事件。
正在载入活动
加载事件是就加载(或加载失败)媒体数据而触发的事件。 这些事件的发生率取决于媒体的加载状态,即是否使用了preload
属性和/或媒体是否已被缓存。
在所有情况下,第一个触发的事件是"loadstart"
事件,这意味着浏览器已开始寻找数据。 但这仅意味着- 并不意味着实际已加载任何数据,甚至不存在媒体资源。
如果preload
属性的值为"none"
,则"loadstart"
事件是在开始播放之前唯一会触发的事件。 而如果preload
属性的值为"metadata"
或"auto"
,那么很快就会再发生两个事件,分别是"progress"
和"loadedmetadata"
。 (在没有预加载的情况下,这些事件仍会触发,但要等到回放开始时才发生。)
"progress"
事件非常复杂,因此我们将在下一部分中单独进行讨论,但是"loadedmetadata"
事件非常简单,因为这仅表示浏览器已加载了足够的元数据以了解媒体的.duration
(作为浮点数,而不是其默认值NaN
)。
当然, "loadedmetadata"
事件只会在媒体能够加载时触发,如果失败(例如,如果src
返回404
),则媒体将产生"error"
事件,并且不再发生可以播放。
在这里,我们再次遇到一些重要的浏览器变体。 在Mobile Safari中,有意不执行preload
设置 ,因此该属性的所有值的行为都与"none"
。 相比之下,在IE10中, 默认情况下始终始终加载媒体元数据 ,因此preload
值"none"
与"metadata"
行为相同。
在触发"loadedmetadata"
之后,下一个重要事件是"canplay"
,浏览器将触发该事件,以指示何时加载了足够的数据,使其知道播放将正常进行(即,它可以播放
)。 如果preload
为"auto"
则在加载几秒钟的数据后将触发"canplay"
事件。 如果preload
是"metadata"
或"none"
,则在开始播放之前不会触发。 此规则的一个例外是Chrome,即使它只是元数据,Chrome始终会在初始预加载期间触发"canplay"
。
还有一个名为"canplaythrough"
的辅助事件,当浏览器估计已加载足够的媒体数据以进行播放而不会中断时,浏览器应触发该事件。 这应该是基于对您的连接速度的估计,因此只有在至少几秒钟的数据被预加载后才应该触发。
但是实际上, "canplaythrough"
事件基本上是没有用的 -因为Safari根本不会触发它,而Opera和Chrome浏览器会在 "canplay"
事件之后立即触发它,即使它尚未预加载多达四分之一的时间第二! 只有Firefox和IE10可以正确实现此事件。
但是您实际上并不需要此事件,因为您可以监视"progress"
事件来确定已预加载了多少数据(如果需要,可以自己计算下载速度):
进度事件
在下载数据的过程中(并且仅在下载数据时), "progress"
事件会持续触发。 因此,当preload
设置为"none"
,它直到播放开始才完全触发。 将preload
设置为"metadata"
,它将在前几秒内触发,然后停止直到播放开始; 如果preload
设置为"auto"
,它将继续触发,直到下载了整个媒体文件。
但是对于所有preload
设置, 一旦开始播放 ,浏览器将继续下载整个媒体文件,并触发连续的"progress"
事件,直到没有剩余要加载的内容为止,即使随后暂停了视频,该事件也会在后台继续。
数据本身由一组时间范围(即时间的不连续部分)表示,因此在我们可以使用"progress"
事件之前,了解这些如何工作至关重要。
首次开始加载媒体时,它将创建一个表示初始部分的时间范围。 因此,例如,一旦加载了前10秒的数据,该时间范围就可以表示为开始时间和结束时间的数组:
[0,10]
但是有可能(实际上很有可能)创建多个时间范围。 例如,如果用户手动寻找超出预加载时间的时间,浏览器将放弃其当前时间范围,并创建一个从该时间点开始的新时间范围(而不是必须将两者之间的所有内容都加载为基本Flash)玩家呢)。
假设用户向前跳了两分钟,然后从那里继续播放,然后再加载10秒,我们就会得到两个范围,我们可以这样表示:
[
[0,10],
[120,130]
]
如果用户然后再次跳回两个范围之间的中间时间,则会创建另一个(第三个)范围:
[
[0,10],
[60,70],
[120,130]
]
然后,一旦该范围的末端到达了最后一个范围的起点,则这些范围将合并在一起:
[
[0,10],
[60,130]
]
这些示例中的数组只是表示形式 ,以帮助解释这一概念–它们不是时间范围数据的实际显示方式; 要获得这种格式的数据,我们必须手动对其进行编译。
媒体有一个表示时间范围的.buffered
对象。 .buffered
对象具有.length
属性以表示有多少个范围,还有一对称为start()
和end()
用于检索单个范围的时间。
因此,要将缓冲的数据转换为这些二维数组,我们可以像这样编译它:
var ranges = [];
for(var i = 0; i < media.buffered.length; i ++)
{
ranges.push([
media.buffered.start(i),
media.buffered.end(i)
]);
}
这就是我们对"progress"
事件的处理方式:
media.addEventListener('progress', function()
{
var ranges = [];
for(var i = 0; i < media.buffered.length; i ++)
{
ranges.push([
media.buffered.start(i),
media.buffered.end(i)
]);
}
}, false);
最终,我们可以使用该数据创建更用户友好的内容,例如可视进度表,如以下演示所示。 它只是包含<div>
<span>
内的一堆定位的<span>
(我们不能使用<progress>
元素,因为它不支持多个范围):
有一些带有"progress"
事件和缓冲数据的值得注意的浏览器怪癖。 首先是从一开始就加载.buffered
数据的不同之.buffered
-尽管大多数浏览器会创建一个时间范围(如本节开头所述), Opera将创建两个范围 ,第一个是预期的,而第二个是最后200ms
时间(大约是最后200ms
)。 因此,如果媒体的长度为两分钟,并且加载的前十秒是这样,则范围将如下所示:
[
[0,10],
[119.8,120]
]
另一个警告是, Mobile Safari不会保留多个范围的数据 -它会丢弃所有活动范围(活动范围(包括当前播放位置)的范围)。 显然,这是故意行为,旨在最大程度地减少媒体元素消耗的内存总量。 因此,再次使用前面的示例,在该示例中,用户向前跳了两分钟,所得到的缓冲数据仍然只包含一个范围:
[
[120,130]
]
这两个怪癖都是值得了解的,但就开发而言,它们通常不会产生太大变化。 但是,另一个更重要的怪癖是,在整个媒体文件都已经预加载的情况下,浏览器的行为。 在这种情况下,大多数浏览器将触发单个"progress"
事件,其中包含表示整个持续时间的单个时间范围。 但是Opera和IE10不提供此进度数据 -Opera触发单个事件,其中缓冲区没有范围(即.buffered.length
为零),而IE10则根本不触发任何"progress"
事件。
在视觉进度表的情况下,这将意味着仪表保持为空而不是被填充。 但它的简单修复仍然使用一个额外的"loadedmetadata"
事件-因为一旦在这些浏览器的事件触发时, .buffered
数据确实现在代表全媒体的持续时间。
计时事件
我们将简要介绍的最后一件事是媒体"timeupdate"
事件,该事件在媒体播放时不断触发。 您将使用此事件将其他内容与媒体播放同步,例如创建手动字幕,在记录中突出显示活动行,甚至用于同步多个媒体源-我在上一篇文章中看到的内容: HTML5视频的可访问音频描述 。
未指定"timeupdate"
事件触发的频率,实际上,在不同的浏览器中,触发频率的差异很大。 但是作为一个总体平均水平,它达到每秒3–5次,对于大多数同步目的来说足够准确。
据我所知,此事件没有浏览器错误或怪癖。 做出不错的改变,嘿!
后记
本文并未涵盖所有可能的媒体事件-还有其他回放和查找事件,高级网络状态事件,甚至还有在音量更改时触发的事件。 但是,我已经介绍了我认为最重要的内容-足以满足您可能要使用视频和音频进行的大多数简单脚本编写工作,并且足以构建基本的自定义界面。
这是最终的参考演示,可帮助您了解这些媒体事件。 它创建了我们讨论过的播放和进度事件的动态日志,显示了与每个事件一起出现的时间和相关属性数据:
From: https://www.sitepoint.com/essential-audio-and-video-events-for-html5/