之前的項目中有使用的video的原因,所以也踩了不少的坑。好久之前的總結了,今天打算找出來共享一下
使用vodeo標簽,目前嘗試了兩種方式,一種是原生的video的標簽,一些東西需要自己手寫功能,另外一個就是使用video.js已經封裝過的項目。
其中也有好多是來自別人的博客,直接搬來用了
- h5原生的video標簽
video標簽的一些屬性
<video
id="my-video"
src="test.mp4"
controls = "" /*禁掉默認控制條-- 必要*/
poster="poster.jpg" /*視頻封面*/
preload="auto" /*預加載*/
webkit-playsinline="true" /*iOS 10中設置可以讓視頻在小窗內播放*/
playsinline="true"
x-webkit-airplay="allow" /*啟用AirPlay支持*/
x5-playsinline
x5-video-player-type="h5" /*對于x5內核聲明啟用同層H5播放器*/
x5-video-player-fullscreen="true" /*全屏設置設置為 true 是防止橫屏*/
x5-video-orientation="portraint" /*播放器的方向,默認為豎屏*/
x5-video-orientation="portraint" /*播放器支付的方向,landscape橫屏,portraint豎屏,默認值為豎屏*/
style="object-fit:fill" /*去除黑邊*/
>
</video>
下面我們來看看這些屬性的作用:
@ src:要嵌到頁面的視頻的URL。可選;你也可以使用video塊內的 <source> 元素來指定需要嵌到頁面的視頻
@ autoplay:布爾屬性;指定后,視頻會馬上自動開始播放,不會停下來等著數據載入結束
@ controls:加上這個屬性,Gecko 會提供用戶控制,允許用戶控制視頻的播放,包括音量,跨幀,暫停/恢復播放
@ poster:一個海報幀的URL,用于在用戶播放或者跳幀之前展示。如果屬性未指定,那么在第一幀可用之前5什么都不會展示;之后第一幀就像海報幀一樣展示
@ preload:該枚舉屬性旨在告訴瀏覽器作者認為達到最佳的用戶體驗的方式是什么。可能是下列值之一:
none:提示作者認為用戶不需要查看該視頻,服務器也想要最小化訪問流量;換句話說就是提示瀏覽器該視頻不需要緩存
metadata:提示盡管作者認為用戶不需要查看該視頻,不過抓取元數據(比如:長度)還是很合理的
auto:用戶需要這個視頻優先加載;換句話說就是提示:如果需要的話,可以下載整個視頻,即使用戶并不一定會用它
空字符串:也就代指 auto 值@ buffered:這個屬性可以讀取到哪段時間范圍內的媒體被緩存了。該屬性包含了一個 TimeRanges 對象
@ played:一個 TimeRanges 對象,指明了視頻已經播放的所有范圍
@ loop:布爾屬性;指定后,會在視頻結尾的地方,自動返回視頻開始的地方
9@muted:布爾屬性,指明了視頻里的音頻的默認設置。設置后,音頻會初始化為靜音。默認值是 false ,意味著視頻播放的時候音頻也會播放
@ height:視頻展示區域的高度,單位是 CSS 像素
@ width:視頻顯示區域的寬度,單位是 CSS 像素
@ crossorigin:該枚舉屬性指明抓取相關圖片是否必須用到CORS(跨域資源共享)。 支持CORS的資源 可在 <canvas>元素中被重用,而不會被污染。允許的值如下:
anonymous:跨域請求會被執行,但是不發送憑證。
use-credentials:跨域請求A cross-origin request會被執行,且憑證會被發送。@ TimeRanges 對象表示事件段,比如,視頻快進的時間段,有一個 length 屬性,表示時間段的個數,有兩個方法 start() 和 end() ,分別返回時間段開始的時間點和結束的時間點
@ webkit-playsinline和playsinline:視頻播放時局域播放,不脫離文檔流 。但是這個屬性比較特別, 需要嵌入網頁的APP比如WeChat中UIwebview 的allowsInlineMediaPlayback = YES webview.allowsInlineMediaPlayback = YES,才能生效。換句話說,如果APP不設置,你頁面中加了這標簽也無效,這也就是為什么安卓手機WeChat 播放視頻總是全屏,因為APP不支持playsinline,而ISO的WeChat卻支持。
這里就要補充下,如果是想做全屏直播或者全屏H5體驗的用戶,IOS需要設置刪除 webkit-playsinline 標簽,因為你設置 false 是不支持的 ,安卓則不需要,因為默認全屏。但這時候全屏是有播放控件的,無論你有沒有設置control。 做直播的可能用得著播放控件,但是全屏H5是不需要的,那么去除全屏播放時候的控件,需要以下設置:同層播放。@ x-webkit-airplay="allow"暫時無法確切的知道其作用,猜測,這個屬性應該是使此視頻支持ios的AirPlay功能。使用AirPlay可以直接從使用iOS的設備上的不同位置播放視頻、音樂還有照片文件,也就是說通過AirPlay功能可以實現影音文件的無線播放,當然前提是播放的終端設備也要支持相應的功能。
@ x5-video-player-type:啟用同層H5播放器,就是在視頻全屏的時候,div可以呈現在視頻層上,也是WeChat安卓版特有的屬性。同層播放別名也叫做沉浸式播放,播放的時候看似全屏,但是已經除去了control和微信的導航欄,只留下"X"和"<"兩鍵。目前的同層播放器只在Android(包括微信)上生效,暫時不支持iOS。至于為什么同層播放只對安卓開放,是因為安卓不能像ISO一樣局域播放,默認的全屏會使得一些界面操作被阻攔,如果是全屏H5還好,但是做直播的話,諸如彈幕那樣的功能就無法實現了,所以這時候同層播放的概念就解決了這個問題。不過在測試的過程中發現,不同版本的IOS和安卓效果略有不同。
x5-video-orientation:聲明播放器支持的方向,可選值landscape 橫屏, portraint豎屏。默認值portraint。無論是直播還是全屏H5一般都是豎屏播放,但是這個屬性需要x5-video-player-type開啟H5模式
@ x5--video--player--fullscreen:全屏設置。它又兩個屬性值,ture和false,true支持全屏播放,false不支持全屏播放。
其實,IOS 微信瀏覽器是Chrome的內核,相關的屬性都支持,也是為什么X5同層播放不支持的原因。安卓微信瀏覽器是X5內核,一些屬性標簽比如playsinline就不支持,所以始終全屏。@ 還有個問題,在Android的微信里面,就算加上了上面的屬性,還會出現上下有黑邊,不能全屏的問題。
解決辦法:給video加上object-fit: fill;的style屬性。如果還是有黑邊有可能是視頻尺寸不合適。
事件交互中主要使用的屬性
- currentTime:播放進行到的時間點,單位為秒
- duration:視頻總時長,單位為秒
監控指標
播放:start:1(首次播放)2(重播)
播放:end:1
播放暫停:pause:1
播放中止:pause:1
快進/快退:Jump:1(快進)2(快退)
錯誤:fail: 1(取回過程);2(當下載時發生錯誤);3(當解碼時發生錯誤);4(不支持音頻/視頻)
播放等待: wait:1
播放時長:totaltime:秒(包含重播)
-
播放中止
具體場景是移動端瀏覽器切換tab導致的隱藏和用戶按home鍵退出瀏覽器html5 提供了 Page Visibility API 來支持監聽tab切換,與之對應新增了
document.hidden 屬性,它顯示頁面是否為用戶當前觀看的頁面,值為 ture 或 false
document.visibilityState 屬性, visible 表示頁面被展現, hidden 表示頁面未被展現, prerender 表示頁面在重新生成,用戶不可見
visibilitychange 事件,監聽頁面在 visible 與 hidden 之間的切換
-
播放時長
起初的思路是獲取到開始播放到停止播放的事件差,記下時間點使用了 currentTime 屬性,主要實現在兩方面playing 時記下時間點startT, pause 和 ended 和 seeked 時記下時間點endT,endT - startT 即播放時長
seeked 時記下時間點startT, seeking 時記下時間點endT,endT - startT 即播放時長
這個思路在 ios 下是看似沒有問題的,但是 android 下確實不行,主要原因是 seeking 事件的監聽沒理解到位,seeking 事件觸發點是用戶目標跳躍到的位置,比如:視頻播放在 0 秒點時,用戶點擊到了 60 秒點處,這是取到的 currentTime 就是 60 ,本來以為會是 0 , ios 下看似沒有問題是因為它的全屏播放模式下,進度條是要拖拽的,不能直接點擊到某個點于是,使用 timeupdate 來獲取 seeking 觸發前的時間點,就可以獲取到相對準確的播放時長了
-
error事件
監聽 error 事件會返回 error.code 來標識錯誤類型:1 = MEDIA_ERR_ABORTED - 取回過程被用戶中止
2 = MEDIA_ERR_NETWORK - 當下載時發生錯誤
3 = MEDIA_ERR_DECODE - 當解碼時發生錯誤
4 = MEDIA_ERR_SRC_NOT_SUPPORTED - 不支持音頻/視頻
獲取橫豎屏的信息
1@ window.onorientationchange = function(){
switch(window.orientation){
case -90:
case 90:
alert("橫屏:" + window.orientation);
case 0:
case 180:
alert("豎屏:" + window.orientation);
break;
}
}
2@ @media (orientation: portrait) { } 橫屏
@media (orientation: landscape) { }豎屏
3@ 旋轉 用css3的屬性
transform: rotate(90deg);
遇到的一些狀況
沒有 <source> 元素且 src 屬性為空時播放會觸發 error 事件,狀態碼為4
解決:忽略 src 屬性為空時的報錯播放結束會觸發暫停
解決:聲明狀態變量,隨著具體操作更新狀態,播放狀態下才會執行暫停操作,結束狀態不執行播放結束后重播會觸發 seeking 和 seeked ,一般瀏覽器觸發一次, android 下uc瀏覽器觸發多次
解決:同上一些瀏覽器監聽不到 seeking 和 seeked
解決:在 timeupdate 里來分析猜測用戶行為一些瀏覽器存在多次連續觸發 seeking + seeked 的情況
解決:時間戳 + 節流 等待最后一次seeking 和 seeked 與 timeupdate 需要保證不會同時執行
解決:監聽到 seeking 觸發,就不再執行 timeupdate 模擬
走過的坑:我曾設想在播放時直接判斷出是否支持 seeking ,方式是播放時設置 currentTime 為 0.01 ,然后檢測 seeking 屬性,后來發現瀏覽器在這樣設置后的 seeking 屬性值不一致個別瀏覽器播放狀態下不觸發 seeking 和 seeked ,但是在重播的時候觸發
解決:聲明狀態變量,隨著具體操作更新狀態,結束狀態不監聽 seeking 觸發
?
默認控件的隱藏
*::-webkit-media-controls-enclosure {
display:none !important;
-webkit-appearance: none;
}
*::-webkit-media-controls-panel {
display: none!important;
-webkit-appearance: none;
}
*::-webkit-media-controls-panel-container {
display: none!important;
-webkit-appearance: none;
}
*::--webkit-media-controls-play-button {
display: none!important;
-webkit-appearance: none;
}
*::-webkit-media-controls-start-playback-button {
display: none!important;
-webkit-appearance: none;
}
*::-webkit-media-controls {
display: none!important;
-webkit-appearance: none;
}
- 使用video.js來處理video標簽
參考video.js的文檔,引入video.js
實例var player = videojs('example_video_1');
videojs是全局函數,它可以接收三個參數(id,options,onready)第三個是回掉函數
- options
有兩種方式可以改變videojs的行為:
- 通過添加video標簽的data-setup屬性:
<video data-setup='{"autoplay":"true",.....}'
var player = videojs('example_video_1',{autoplay:true,....}) ,
直接傳入options
由于第一種方式是寫在html標簽中,通過JSON.parse解析,性能低,還容易報錯。個人更傾向于方法2.
這里有大量關于options的配置參數:http://docs.videojs.com/tutorial-options.html
常用幾個項有:
autoplay : true/false
//播放器準備好之后,是否自動播放 【默認false】If true/present as an attribute, begins playback when the player is ready
controls : true/false
//是否擁有控制條 【默認true】,如果設為false ,那么只能通過api進行控制了。也就是說界面上不會出現任何控制按鈕
height: 300px
//視頻容器的高度,字符串或數字 單位像素 比如: height:300 or height:'300px'
width: 300px
//視頻容器的寬度, 字符串或數字 單位像素
loop : true/false //視頻播放結束后,是否循環播放
muted : true/false //是否靜音
poster: 播放前顯示的視頻畫面,播放開始之后自動移除。通常傳入一個URL
preload:auto//預加載
auto //自動
metadata //元數據信息 ,比如視頻長度,尺寸等
none //不預加載任何數據,直到用戶開始播放才開始下載
children: Array | Object
//可選子組件 從基礎的Component組件繼承而來的子組件,數組中的順序將影響組件的創建順序哦。
// 下面的方式只使用bigPlayButton和controlBar兩個子組件
videojs('my-player', {
children: [
'bigPlayButton',
'controlBar'
]
});
sources:Array //資源文件
videojs('my-player', {
sources: [{
src: '//path/to/video.mp4',
type: 'video/mp4'
}, {
src: '//path/to/video.webm',
type: 'video/webm'
}]
});
等價于html中的形式:
<video ...>
<source src="http://path/to/video.mp4" type="video/mp4">
<source src="http://path/to/video.webm" type="video/webm">
</video>
techOrder: Array //使用哪種技術播放,可選值有'html5','flash' 默認為['html5'], 注意: 在v6.0.0 及以上的版本中,默認不包含flash的使用代碼。如果要使用flash播放的,需要手動引入flash相關邏輯的代碼。且需要指定swf文件的路徑。
// 全局指定swf文件的位置
videojs.options.flash.swf = 'video-js.swf'
// Create a player.
var player = videojs('example_video_1',{
teachOrder:['flash']
},function(){
console.log(this)
}
});
方法
autoplay
buffered
bufferedEnd
bufferedPercent
cancelFullScreen deprecated
controls
currentSrc
currentTime
currentType
dispose
duration
ended
error
exitFullscreen
init
isFullScreen deprecated
isFullscreen
language
load
loop
muted
pause
paused
play
playbackRate
poster
preload
remainingTime
requestFullScreen deprecated
requestFullscreen
seeking
src
volume
addChild inherited
addClass inherited
buildCSSClass inherited
children inherited
contentEl inherited
createEl inherited
dimensions inherited
el inherited
enableTouchActivity inherited
getChild inherited
getChildById inherited
hasClass inherited
height inherited
hide inherited
id inherited
initChildren inherited
name inherited
off inherited
on inherited
one inherited
options inherited
player inherited
ready inherited
removeChild inherited
removeClass inherited
show inherited
trigger inherited
triggerReady inherited
width inherited
事件
durationchange
ended
firstplay
fullscreenchange
loadedalldata
loadeddata
loadedmetadata
loadstart
pause
play
progress
seeked
seeking
timeupdate
volumechange
waiting
resize inherited
組件
Player
PosterImage
TextTrackDisplay
LoadingSpinner
BigPlayButton
ControlBar
PlayToggle
FullscreenToggle
CurrentTimeDisplay
TimeDivider
DurationDisplay
RemainingTimeDisplay
ProgressControl
SeekBar
LoadProgressBar
PlayProgressBar
SeekHandle
VolumeControl
VolumeBar
VolumeLevel
VolumeHandle
MuteToggle
//eg:移除靜音按鈕
var player = videojs(‘video-id‘, {
controlBar: {
muteToggle: false
}
});
自定義組件(低版本不支持)
// Get the Component base class from Video.js
// 從Videojs中獲取一個基礎組件
var Component = videojs.getComponent('Component');
// The videojs.extend function is used to assist with inheritance. In
// an ES6 environment, `class TitleBar extends Component` would work
// identically.
// videojs.extend方法用來實現繼承,等同于ES6環境中的class titleBar extends Component用法
var TitleBar = videojs.extend(Component, {
// The constructor of a component receives two arguments: the
// player it will be associated with and an object of options.
// 這個構造函數接收兩個參數:
// player將被用來關聯options中的參數
constructor: function(player, options) {
// It is important to invoke the superclass before anything else,
// to get all the features of components out of the box!
// 在做其它事之前先調用父類的構造函數是很重要的,
// 這樣可以使父組件的所有特性在子組件中開箱即用。
Component.apply(this, arguments);
// If a `text` option was passed in, update the text content of
// the component.
// 如果在options中傳了text屬性,那么更新這個組件的文字顯示
if (options.text) {
this.updateTextContent(options.text);
}
},
// The `createEl` function of a component creates its DOM element.
// 創建一個DOM元素
createEl: function() {
return videojs.dom.createEl('div', {
// Prefixing classes of elements within a player with "vjs-"
// is a convention used in Video.js.
//給元素加vjs-開頭的樣式名,是videojs內置樣式約定俗成的做法
className: 'vjs-title-bar'
});
},
// This function could be called at any time to update the text
// contents of the component.
// 這個方法可以在任何需要更新這個組件內容的時候調用
updateTextContent: function(text) {
// If no text was provided, default to "Text Unknown"
// 如果options中沒有提供text屬性,默認顯示Text Unknow
if (typeof text !== 'string') {
text = 'Text Unknown';
}
// Use Video.js utility DOM methods to manipulate the content
// of the component's element.
// 使用Video.js提供的DOM方法來操作組件元素
videojs.dom.emptyEl(this.el());
videojs.dom.appendContent(this.el(), text);
}
});
// Register the component with Video.js, so it can be used in players.
// 在videojs中注冊這個組件,才可以使用哦.
videojs.registerComponent('TitleBar', TitleBar);
//使用組件
player.addChild('TitleBar', {text: '這里是標題'});
我自己在github上的總結以及一些demo
參考資料
原生video
videojs 文檔
videojs github
html5--移動端視頻video的android兼容,去除播放控件、全屏等
移動端手機網頁強制橫屏或全屏模仿橫評的js和css3方法
videojs 使用以及創建組件
videojs的使用 方法 事件 比較全
video.js--很贊的H5視頻播放庫
video.js 的文檔