新聞中心
HTML 5中的視頻和音頻中有不少核心的事件,其中有的比較容易理解,基本能從字面就解析了,比如“play”事件就很好理解。而其他有的事件則需要花費點心思,特別是“progress”事件。因此,在本文中,將帶領讀者研究HTML 5視頻和音頻中重要的事件,探究這些事件是應該在什么時候使用以及其中的重要相關屬性。我們也將看下這些事件在當今不同瀏覽器中的差異。為了本文的測試,使用的瀏覽器如下:Opera 12、Chrome 28、IE 10、Firefox 22、Safari 5(桌面版)和Mobile Safari 6(iOS版本)。

播放相關的事件
我們先來看下視頻和音頻中的playback事件。plackback事件發(fā)生的時機在播放媒體或停止媒體中,這個比較好理解,下面來仔細研究一下。其中“play”和“pause”事件分別在媒體播放和停止的時候觸發(fā),但也有一個“ended”事件,該事件是在媒體播放完畢到達***的時候觸發(fā)-無論是正常情況下的結束或是用戶自己動播放條到媒體文件的***。
這兩個對應事件的觸發(fā)很簡單,只需要調用play()和pause()方法就可以了。它們也有對應的屬性,其中.paused屬性默認設置為true,而.ended屬性默認是設置為false的,當然當媒體文件播放到***的時候,.ended屬性則變?yōu)閠rue。
然而,在Opera、Safari和IE 10中,請注意有個區(qū)別,就是即使媒體播放完畢,其中的.paused屬性依然為false(從邏輯上說,媒體已經播放完畢了)。這樣一個實際的結果是play/pause按鈕的事件都不會觸發(fā)了,看下面的例子:
- button.addEventListener('click', function(e)
- {
- if(media.paused)
- {
- media.play();
- }
- else
- {
- media.pause();
- }
- }, false);
也就是說,當視頻結束后,其pause()事件依然能調用。我們可以通過一個小技巧去修正這個問題,如下代碼所示:
- media.addEventListener('ended', function(e)
- {
- media.pause();
- }, false);
也就是在監(jiān)聽ended事件中,主動調用media.pause()方法就可以了。在Firefox和Chrome的***版本中,已經修正了這個問題。
關于加載事件
加載事件總是在媒體加載(或加載失敗)的時候發(fā)生。加載事件受到加載媒體狀態(tài)的影響,比如是否使用了preload屬性又或者是媒體是否緩存了。下面我們來逐一分析下其中關鍵的事件,首先是“l(fā)oadstart”事件,含義是讓瀏覽器剛開始的時候去尋找獲得數(shù)據(jù)。但要注意的是,loadstart并不意味著就任何數(shù)據(jù)就會馬上加載,還要看preload屬性的值的設置。如果preload(預裝載的值)設置為“none”的話,則“l(fā)oadstart”事件就是唯一在播放視頻前觸發(fā)的事件。如果preload屬性設置為“metadata”或者是“auto”,則會觸發(fā)“progress”和“l(fā)oadmetadata”事件(如果沒預加載的話,這兩個事件也會觸發(fā),但不會在播放前發(fā)生)。
在下一節(jié)中,我們才學習“progress”事件,由于其比較復雜。我們先來學習比較簡單的“l(fā)oadedmetadata”事件。正如字面的含義,瀏覽器僅僅加載媒體的元數(shù)據(jù)信息而已,比如其長度.duration(是一個浮點數(shù)而不是默認的NaN)。
當然,“l(fā)oadedmetadata”事件只有在確認能加載媒體文件后才能加載,換句話說,如果某個媒體問題文件不能加載(比如404錯誤),則會直接產生error事件,當然也不可能繼續(xù)運行相關的播放事件了。
這里,又要提醒用戶有的瀏覽器中是有差別的。在Mobile Safari中,preload的屬性其實是沒聲明的,就等于設置為“none”一樣了。但在IE 10中,又有不同了,其中媒體的元數(shù)據(jù)默認是自動加載的,所以preload設置為none其實跟設置為metadata的作用是一樣的。
在“l(fā)oadedmetadata”事件觸發(fā)后,接下來的重要事件就是“canplay”,這個是瀏覽器用來確認是否已經裝載足夠的媒體信息到瀏覽器中能播放一個事件。如果preload屬性設置為“auto”,則在數(shù)據(jù)裝載后大概幾秒,“canplay”事件就會觸發(fā);如果preload設置為“metadata”或者是“none”,則直到播放開始時才會觸發(fā)。這個規(guī)則對Chrome瀏覽器來說是例外的,在Chrome中,即使只是加載元數(shù)據(jù)階段,“canplay”事件也會觸發(fā)。
同時也有僅接著的事件叫“canplaythrough”,這個事件其實是給瀏覽器根據(jù)當前網絡狀況去判斷是否已經加載了足夠的媒體片斷而支撐基本的播放。所以這個事件要在數(shù)據(jù)被預加載大概幾秒后才會觸發(fā)。
在實際情況下,“canplaythrough”事件基本是沒啥作用的,因為Safari根本不會觸發(fā)這個事件,而Opera和Chrome則在“canplay”事件觸發(fā)后馬上就觸發(fā)這個事件,只有FireFox和IE 10實現(xiàn)了這個事件。
所以實際上,開發(fā)者最應該關心的是監(jiān)視“progress”事件,以了解媒體到底加載了多少(必要的時候可以計算媒體的下載速度)。
Progress事件
接下來我們重點學習下progress事件。該事件在數(shù)據(jù)正在下載的時候會觸發(fā)。所以當preload設置為none的時候,progress事件在知道播放事件真正開始前是不會觸發(fā)的。如果preload設置為“metadata”,則該事件會短暫觸發(fā)大概幾秒,然后停止,直到真正的播放行為開始時才觸發(fā);如果preload設置為“auto”,則會觸發(fā)一直直到整個媒體文件下載完畢。
無論preload如何設置,一旦用戶開始進行播放的行為,則瀏覽器會開始下載整個媒體文件,則會持續(xù)觸發(fā)progress事件,一直直到整個文件下載完畢,即使視頻被暫停。
當數(shù)據(jù)下載后,則我們需要了解如何用時間表達這個progress的事件,則對接下來深入了解progress事件是十分重要的。當數(shù)據(jù)開始加載時,會創(chuàng)建表示媒體播放時間的范圍,比如一旦頭10秒的數(shù)據(jù)已經加載,則以數(shù)組的方式記錄了開始和結束時間,如下的方式表示:
- [0,10]
當然,實際上是會有多個時間范圍存在的,比如用戶手工使用播放器的進度條去移動到想要的位置,則瀏覽器會忽略當前的時間范圍而加載新的部分而不是象 Flash那樣重新加載兩個時間點之間的部分。
比如我們加載10秒的視頻后,跳到兩分鐘后的片斷繼續(xù)播放另外的10秒,則有兩個時間范圍,用如下的方式表達:
- [
- [0,10],
- [120,130]
- ]
假設用戶這個時候又回看舊的片斷,則繼續(xù)又增加一個時間范圍的數(shù)組,如:
- [
- [0,10],
- [60,70],
- [120,130]
- ]
如果從60秒開始看到130秒結束,則***的時間范圍合拼為:
- [
- [0,10],
- [60,130]
- ]
上面的例子只是簡單說明在播放媒體中,時間的范圍是如何運作的,當然實際上的數(shù)據(jù)保存不是這個樣子。實際上媒體都有一個.buffered對象以表示時間范圍。.buffered對象有一個.length長度屬性表示有多少段時間范圍,并且有對應的start()和end()方法, 所以我們可以將buffered的數(shù)據(jù)轉換為二維數(shù)組,如下代碼所示:
- 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);
有了這個方法,則我們可以針對progress事件進行一些定制開發(fā)。比如我們可以實現(xiàn)一個簡單的播放視頻,并且提供一個額外的進度條,在視頻加載過程中能看到其進度。其實際的運行效果請參考:http://jspro.brothercake.com/media-events/progress.html ,下面是一個運行效果截圖:
下面選取其中的代碼片斷講解其核心原理:
- function onprogress()
- {
- //獲得buffered數(shù)據(jù)
- var ranges = [];
- for(var i = 0; i < media.buffered.length; i ++)
- {
- ranges.push([
- media.buffered.start(i),
- media.buffered.end(i)
- ]);
- }
- //獲得在容器中的當前進度
- var spans = progress.getElementsByTagName('span');
- //如果還沒有加載完畢則繼續(xù)加載
- while(spans.length < media.buffered.length)
- {
- progress.appendChild(document.createElement('span'));
- }
- while(spans.length > media.buffered.length)
- {
- progress.removeChild(progress.lastChild);
- }
- for(var i = 0; i < media.buffered.length; i ++)
- {
- spans[i].style.left = Math.round
- (
- (100 / media.duration) *
- ranges[i][0]
- )
- + '%';
- spans[i].style.width = Math.round
- (
- (100 / media.duration) *
- (ranges[i][1] - ranges[i][0])
- )
- + '%';
- }
- }
在上面的代碼段中,使用的是設置的寬度去代表進度條的每一個格的寬度,首先獲得buffered數(shù)據(jù),存放到數(shù)據(jù)ranges中,然后判斷媒體文件是否加載完畢,如果還沒加載完畢,則繼續(xù)往DOM中添加標簽,而這個span標簽的寬度和樣式則是通過上面的代碼按百分比計算出來。
***,用戶可以通過http://jspro.brothercake.com/media-events/events.html的例子,學習到本文中提到的在媒體加載前、加載中和加載后瀏覽器的相關事件,在這個例子中會輸出相關的日志。
當前文章:HTML5中視頻和音頻核心事件講解
分享URL:http://m.fisionsoft.com.cn/article/dpeghij.html


咨詢
建站咨詢
