深入理解Video標(biāo)簽

作者 | 華仔專業(yè)打雜
鏈接 | https://juejin.cn/post/6979881494189703199
Video相關(guān)屬性
1、自動播放
{/* 自動播放 */}<video ref={ videoRef } controls autoPlay />
為了解決自動播放失敗,在這里介紹兩種方法解決 Autoplay 限制的方案
播放失敗時(shí)繞過 Autoplay 限制
直接繞過 Autoplay 限制
1.1 播放失敗時(shí)繞過 Autoplay 限制
在實(shí)際使用中,頁面并不是完全被 Autoplay 限制,隨著用戶使用這個(gè)頁面的次數(shù)增加,瀏覽器會將這個(gè)頁面加入自己的 Autoplay 白名單列表中。
根據(jù)這個(gè)原理,可在檢測到播放失敗時(shí),引導(dǎo)用戶點(diǎn)擊頁面上的某個(gè)位置來恢復(fù)播放。(Google瀏覽器下測試均播放失敗)
// 可播放監(jiān)聽。當(dāng)瀏覽器能夠開始播放指定的音頻/視頻時(shí)觸發(fā)this.videoRef.addEventListener('canplay', () => {console.log('視頻可以播放了')setTimeout(() => {// this.videoRef.paused 判斷是否暫停,用來判斷是視頻是否在播放中,如果沒有播放就console.log(this.videoRef.paused)console.log('視頻是否在暫停中', this.videoRef.paused)this.isPlay = !this.videoRef.paused}, 500)})// 通過promise來判斷是否在播放const videoPromise = this.videoRef.play()if(!!videoPromise) {videoPromise.then(() => {this.isPlay = trueconsole.log('播放成功')}).catch(() => {this.isPlay = falseconsole.log('播放失敗')})} else {// 此時(shí)可以通過canplay 監(jiān)聽是否在播放}
1.2 直接繞過 Autoplay 限制
可以通過如下兩種方案實(shí)現(xiàn)直接繞過 Autoplay 限制
在video標(biāo)簽中關(guān)閉靜音muted屬性設(shè)置為true。媒體不包含聲音時(shí)不會被 Autoplay 限制。(引導(dǎo)用戶開啟聲音)
UI上引導(dǎo)用戶觸發(fā)播放
注意:無論使用哪種方案,在自動播放策略的限制下,沒有用戶操作之前都不可能自動播放有聲媒體。
雖然瀏覽器會在本地維護(hù)一個(gè)白名單來決定對哪些網(wǎng)站解除自動播放限制,但該白名單無法通過 JavaScript 探測到。
// 移動端document.body.addEventListener('touchstart', () => {console.log('觸發(fā)播放')this.videoRef.play();})// PC端document.body.addEventListener('mousedown', () => {console.log('觸發(fā)播放')this.videoRef.play();})// 微信端IOS手機(jī)下觸發(fā)自動播放,大部分IOS能正常自動播放(安卓機(jī)只能通過touchstart觸發(fā)播放)document.body.addEventListener('WeixinjsBridgeReady', () => {console.log('觸發(fā)播放')this.videoRef.play();})
注意: Safari 只允許通過用戶交互來觸發(fā)有聲媒體的播放,而不是在用戶交互后就打開 Autoplay 限制。
2、播放時(shí)間屬性控制
首先我們來看一段代碼,在Google端能夠正常播放,但是在移動端和Safari中還是重頭開始播放。使用了canplay 和loadedmetadata,oncanplay事件來判斷視頻狀態(tài)再設(shè)置currentTime,但移動端還是無效。
const videoPromise = this.videoRef.play()this.videoRef.currentTime = 10
解決方案:
設(shè)置視頻的Timeupdate事件監(jiān)聽設(shè)置播放時(shí)間
使用定時(shí)器設(shè)置播放時(shí)間
this.videoRef.play();// 通過時(shí)間更新播放時(shí)間let timer = setTimeout(function(){// 這里還是有一定的缺陷,如果用戶觸發(fā)了視頻播放,但是加載比較長就會有問題this.videoRef.currentTime = 需要設(shè)置的時(shí)間;clearTimeout(timer);},200);// timeupdate:目前的播放位置已更改時(shí),播放時(shí)間更新this.videoRef.addEventListener('timeupdate', (e) => {console.log('timeupdate')let timeDisplay = Math.floor(this.videoRef.currentTime);if(timeDisplay > 0){if(this.firstOpen){this.videoRef.currentTime = 10;this.firstOpen = false;}}})// seeking:查找開始。當(dāng)用戶開始移動/跳躍到音頻/視頻中新的位置時(shí)觸發(fā)this.videoRef.addEventListener('seeking', (e) => {// 在這里處理視頻播放是否播到放指定的時(shí)間console.log('開始移動進(jìn)度條', this.videoRef.currentTime)})// seeked:查找結(jié)束。當(dāng)用戶已經(jīng)移動/跳躍到視頻中新的位置時(shí)觸發(fā)this.videoRef.addEventListener('seeked', (e) => {console.log('進(jìn)度條已經(jīng)移動到了新的位置', this.videoRef.currentTime)})
其他屬性介紹:
this.videoRef.error; //null:正常
this.videoRef.error.code; //1.用戶終止 2.網(wǎng)絡(luò)錯誤 3.解碼錯誤 4.URL無效
this.videoRef.networkState; //0.此元素未初始化 1.正常但沒有使用網(wǎng)絡(luò) 2.正在下載數(shù)據(jù) 3.沒有找到資源
this.videoRef.buffered; //返回已緩沖區(qū)域,TimeRanges
this.videoRef.paused; //是否暫停
this.videoRef.defaultPlaybackRate = value;//默認(rèn)的回放速度,可以設(shè)置
this.videoRef.playbackRate = value;//當(dāng)前播放速度,設(shè)置后馬上改變
this.videoRef.played; //返回已經(jīng)播放的區(qū)域,TimeRanges,
this.videoRef.seekable; //返回可以seek的區(qū)域 TimeRanges
this.videoRef.ended; //是否結(jié)束
Video相關(guān)事件
了解Video 標(biāo)簽相關(guān)事件觸發(fā)時(shí)機(jī),才能處理好業(yè)務(wù)相關(guān)邏輯。
// loadstart 視頻查找。當(dāng)瀏覽器開始尋找指定的音頻/視頻時(shí)觸發(fā),也就是當(dāng)加載過程開始時(shí)this.videoRef.addEventListener('loadstart', (e) => {console.log('提示視頻的元數(shù)據(jù)已加載')// console.log(e)console.log(this.videoRef.duration) // NaN})// durationchange 時(shí)長變化。當(dāng)指定的音頻/視頻的時(shí)長數(shù)據(jù)發(fā)生變化時(shí)觸發(fā),加載后,時(shí)長由 NaN 變?yōu)橐纛l/視頻的實(shí)際時(shí)長this.videoRef.addEventListener('durationchange', (e) => {console.log('提示視頻的時(shí)長已改變')console.log(this.videoRef.duration) // 視頻的實(shí)際時(shí)長(單位:秒)})// loadedmetadata :元數(shù)據(jù)加載。當(dāng)指定的音頻/視頻的元數(shù)據(jù)已加載時(shí)觸發(fā),元數(shù)據(jù)包括:時(shí)長、尺寸(僅視頻)以及文本軌道this.videoRef.addEventListener('loadedmetadata', (e) => {console.log('提示視頻的元數(shù)據(jù)已加載')// console.log(e)})// loadeddata:視頻下載監(jiān)聽。當(dāng)當(dāng)前幀的數(shù)據(jù)已加載,但沒有足夠的數(shù)據(jù)來播放指定音頻/視頻的下一幀時(shí)觸發(fā)this.videoRef.addEventListener('loadeddata', (e) => {console.log('提示當(dāng)前幀的數(shù)據(jù)是可用的')})// progress:瀏覽器下載監(jiān)聽。當(dāng)瀏覽器正在下載指定的音頻/視頻時(shí)觸發(fā)this.videoRef.addEventListener('progress', (e) => {console.log('提示視頻正在下載中')})// canplay:可播放監(jiān)聽。當(dāng)瀏覽器能夠開始播放指定的音頻/視頻時(shí)觸發(fā)this.videoRef.addEventListener('canplay', (e) => {console.log('視頻可以播放了')setTimeout(() => {// this.videoRef.paused 判斷是否暫停console.log('視頻是否在暫停中', this.videoRef.paused)this.isPlay = !this.videoRef.paused}, 1000)})// canplaythrough:可流暢播放。當(dāng)瀏覽器預(yù)計(jì)能夠在不停下來進(jìn)行緩沖的情況下持續(xù)播放指定的音頻/視頻時(shí)觸發(fā)this.videoRef.addEventListener('canplaythrough', (e) => {console.log('提示視頻能夠不停頓地一直播放')console.log(e)})// play: 播放監(jiān)聽this.videoRef.addEventListener('play', (e) => {console.log('提示該視頻正在播放中')})// pause:暫停監(jiān)聽this.videoRef.addEventListener('pause', (e) => {console.log('暫停播放')})// seeking:查找開始。當(dāng)用戶開始移動/跳躍到音頻/視頻中新的位置時(shí)觸發(fā)this.videoRef.addEventListener('seeking', (e) => {// 在這里處理到底有沒有更新到最新的位置console.log('開始移動進(jìn)度條', this.videoRef.currentTime)})// seeked:查找結(jié)束。當(dāng)用戶已經(jīng)移動/跳躍到視頻中新的位置時(shí)觸發(fā)this.videoRef.addEventListener('seeked', (e) => {console.log('進(jìn)度條已經(jīng)移動到了新的位置', this.videoRef.currentTime)})// waiting:視頻加載等待。當(dāng)視頻由于需要緩沖下一幀而停止,等待時(shí)觸發(fā)this.videoRef.addEventListener('waiting', (e) => {console.log('視頻加載等待')console.log(e)})// playing:當(dāng)視頻在已因緩沖而暫?;蛲V购笠丫途w時(shí)觸發(fā)this.videoRef.addEventListener('playing', (e) => {console.log('playing')console.log(e)})// timeupdate:目前的播放位置已更改時(shí),播放時(shí)間更新this.videoRef.addEventListener('timeupdate', (e) => {// console.log('timeupdate')// let timeDisplay = Math.floor(this.videoRef.currentTime);// if(timeDisplay > 0){// if(this.firstOpen){// this.videoRef.currentTime = 10;// this.firstOpen = false;// }// }})// ended:播放結(jié)束this.videoRef.addEventListener('ended', (e) => {console.log('視頻播放完了')console.log(e)})// error:播放錯誤this.videoRef.addEventListener('error', (e) => {console.log('視頻出錯了')console.log(e)})// volumechange:當(dāng)音量更改時(shí)this.videoRef.addEventListener('volumechange', (e) => {console.log('volumechange')console.log(e)})// stalled:當(dāng)瀏覽器嘗試獲取媒體數(shù)據(jù),但數(shù)據(jù)不可用時(shí)this.videoRef.addEventListener('stalled', (e) => {console.log('stalled')console.log(e)})// ratechange:當(dāng)視頻的播放速度已更改時(shí)this.videoRef.addEventListener('ratechange', (e) => {console.log('ratechange')console.log(e)})
關(guān)于微信H5視頻兼容介紹: 移動端兼容
最后
移動端Web對于Video自動播放的兼容性是在太差,尤其安卓。各種問題,各種兼容,各種心累。
本文到此結(jié)束。希望對你有幫助。
學(xué)習(xí)更多技能
請點(diǎn)擊下方公眾號
![]()

