之前的項目中有使用的video的原因雏赦,所以也踩了不少的坑劫笙。好久之前的總結(jié)了芙扎,今天打算找出來共享一下
使用vodeo標簽,目前嘗試了兩種方式邀摆,一種是原生的video的標簽,一些東西需要自己手寫功能伍茄,另外一個就是使用video.js已經(jīng)封裝過的項目栋盹。
其中也有好多是來自別人的博客,直接搬來用了
- h5原生的video標簽
video標簽的一些屬性
<video
id="my-video"
src="test.mp4"
controls = "" /*禁掉默認控制條-- 必要*/
poster="poster.jpg" /*視頻封面*/
preload="auto" /*預加載*/
webkit-playsinline="true" /*iOS 10中設置可以讓視頻在小窗內(nèi)播放*/
playsinline="true"
x-webkit-airplay="allow" /*啟用AirPlay支持*/
x5-playsinline
x5-video-player-type="h5" /*對于x5內(nèi)核聲明啟用同層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塊內(nèi)的 <source> 元素來指定需要嵌到頁面的視頻
@ autoplay:布爾屬性收壕;指定后,視頻會馬上自動開始播放轨蛤,不會停下來等著數(shù)據(jù)載入結(jié)束
@ controls:加上這個屬性蜜宪,Gecko 會提供用戶控制,允許用戶控制視頻的播放祥山,包括音量圃验,跨幀,暫停/恢復播放
@ poster:一個海報幀的URL缝呕,用于在用戶播放或者跳幀之前展示澳窑。如果屬性未指定,那么在第一幀可用之前5什么都不會展示供常;之后第一幀就像海報幀一樣展示
@ preload:該枚舉屬性旨在告訴瀏覽器作者認為達到最佳的用戶體驗的方式是什么摊聋。可能是下列值之一:
none:提示作者認為用戶不需要查看該視頻栈暇,服務器也想要最小化訪問流量栗精;換句話說就是提示瀏覽器該視頻不需要緩存
metadata:提示盡管作者認為用戶不需要查看該視頻,不過抓取元數(shù)據(jù)(比如:長度)還是很合理的
auto:用戶需要這個視頻優(yōu)先加載瞻鹏;換句話說就是提示:如果需要的話悲立,可以下載整個視頻,即使用戶并不一定會用它
空字符串:也就代指 auto 值@ buffered:這個屬性可以讀取到哪段時間范圍內(nèi)的媒體被緩存了新博。該屬性包含了一個 TimeRanges 對象
@ played:一個 TimeRanges 對象薪夕,指明了視頻已經(jīng)播放的所有范圍
@ loop:布爾屬性;指定后赫悄,會在視頻結(jié)尾的地方原献,自動返回視頻開始的地方
9@muted:布爾屬性馏慨,指明了視頻里的音頻的默認設置。設置后姑隅,音頻會初始化為靜音写隶。默認值是 false ,意味著視頻播放的時候音頻也會播放
@ height:視頻展示區(qū)域的高度,單位是 CSS 像素
@ width:視頻顯示區(qū)域的寬度讲仰,單位是 CSS 像素
@ crossorigin:該枚舉屬性指明抓取相關(guān)圖片是否必須用到CORS(跨域資源共享)慕趴。 支持CORS的資源 可在 <canvas>元素中被重用,而不會被污染鄙陡。允許的值如下:
anonymous:跨域請求會被執(zhí)行冕房,但是不發(fā)送憑證。
use-credentials:跨域請求A cross-origin request會被執(zhí)行趁矾,且憑證會被發(fā)送耙册。@ TimeRanges 對象表示事件段模聋,比如皿桑,視頻快進的時間段,有一個 length 屬性截酷,表示時間段的個數(shù)蔓同,有兩個方法 start() 和 end() 溪厘,分別返回時間段開始的時間點和結(jié)束的時間點
@ webkit-playsinline和playsinline:視頻播放時局域播放,不脫離文檔流 牌柄。但是這個屬性比較特別畸悬, 需要嵌入網(wǎng)頁的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功能可以實現(xiàn)影音文件的無線播放,當然前提是播放的終端設備也要支持相應的功能蜒茄。
@ x5-video-player-type:啟用同層H5播放器唉擂,就是在視頻全屏的時候餐屎,div可以呈現(xiàn)在視頻層上檀葛,也是WeChat安卓版特有的屬性。同層播放別名也叫做沉浸式播放腹缩,播放的時候看似全屏屿聋,但是已經(jīng)除去了control和微信的導航欄,只留下"X"和"<"兩鍵藏鹊。目前的同層播放器只在Android(包括微信)上生效润讥,暫時不支持iOS。至于為什么同層播放只對安卓開放盘寡,是因為安卓不能像ISO一樣局域播放楚殿,默認的全屏會使得一些界面操作被阻攔,如果是全屏H5還好竿痰,但是做直播的話脆粥,諸如彈幕那樣的功能就無法實現(xiàn)了,所以這時候同層播放的概念就解決了這個問題影涉。不過在測試的過程中發(fā)現(xiàn)变隔,不同版本的IOS和安卓效果略有不同。
x5-video-orientation:聲明播放器支持的方向蟹倾,可選值landscape 橫屏, portraint豎屏匣缘。默認值portraint。無論是直播還是全屏H5一般都是豎屏播放鲜棠,但是這個屬性需要x5-video-player-type開啟H5模式
@ x5--video--player--fullscreen:全屏設置肌厨。它又兩個屬性值,ture和false豁陆,true支持全屏播放夏哭,false不支持全屏播放。
其實献联,IOS 微信瀏覽器是Chrome的內(nèi)核竖配,相關(guān)的屬性都支持何址,也是為什么X5同層播放不支持的原因。安卓微信瀏覽器是X5內(nèi)核进胯,一些屬性標簽比如playsinline就不支持用爪,所以始終全屏。@ 還有個問題胁镐,在Android的微信里面偎血,就算加上了上面的屬性,還會出現(xiàn)上下有黑邊盯漂,不能全屏的問題颇玷。
解決辦法:給video加上object-fit: fill;的style屬性。如果還是有黑邊有可能是視頻尺寸不合適就缆。
事件交互中主要使用的屬性
- currentTime:播放進行到的時間點帖渠,單位為秒
- duration:視頻總時長,單位為秒
監(jiān)控指標
播放:start:1(首次播放)2(重播)
播放:end:1
播放暫停:pause:1
播放中止:pause:1
快進/快退:Jump:1(快進)2(快退)
錯誤:fail: 1(取回過程)竭宰;2(當下載時發(fā)生錯誤)空郊;3(當解碼時發(fā)生錯誤);4(不支持音頻/視頻)
播放等待: wait:1
播放時長:totaltime:秒(包含重播)
-
播放中止
具體場景是移動端瀏覽器切換tab導致的隱藏和用戶按home鍵退出瀏覽器html5 提供了 Page Visibility API 來支持監(jiān)聽tab切換切揭,與之對應新增了
document.hidden 屬性狞甚,它顯示頁面是否為用戶當前觀看的頁面,值為 ture 或 false
document.visibilityState 屬性廓旬, visible 表示頁面被展現(xiàn)哼审, hidden 表示頁面未被展現(xiàn), prerender 表示頁面在重新生成孕豹,用戶不可見
visibilitychange 事件涩盾,監(jiān)聽頁面在 visible 與 hidden 之間的切換
-
播放時長
起初的思路是獲取到開始播放到停止播放的事件差,記下時間點使用了 currentTime 屬性巩步,主要實現(xiàn)在兩方面playing 時記下時間點startT旁赊, pause 和 ended 和 seeked 時記下時間點endT,endT - startT 即播放時長
seeked 時記下時間點startT椅野, seeking 時記下時間點endT终畅,endT - startT 即播放時長
這個思路在 ios 下是看似沒有問題的,但是 android 下確實不行竟闪,主要原因是 seeking 事件的監(jiān)聽沒理解到位离福,seeking 事件觸發(fā)點是用戶目標跳躍到的位置,比如:視頻播放在 0 秒點時炼蛤,用戶點擊到了 60 秒點處妖爷,這是取到的 currentTime 就是 60 ,本來以為會是 0 , ios 下看似沒有問題是因為它的全屏播放模式下絮识,進度條是要拖拽的绿聘,不能直接點擊到某個點于是,使用 timeupdate 來獲取 seeking 觸發(fā)前的時間點次舌,就可以獲取到相對準確的播放時長了
-
error事件
監(jiān)聽 error 事件會返回 error.code 來標識錯誤類型:1 = MEDIA_ERR_ABORTED - 取回過程被用戶中止
2 = MEDIA_ERR_NETWORK - 當下載時發(fā)生錯誤
3 = MEDIA_ERR_DECODE - 當解碼時發(fā)生錯誤
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@ 旋轉(zhuǎn) 用css3的屬性
transform: rotate(90deg);
遇到的一些狀況
沒有 <source> 元素且 src 屬性為空時播放會觸發(fā) error 事件熄攘,狀態(tài)碼為4
解決:忽略 src 屬性為空時的報錯播放結(jié)束會觸發(fā)暫停
解決:聲明狀態(tài)變量,隨著具體操作更新狀態(tài)彼念,播放狀態(tài)下才會執(zhí)行暫停操作挪圾,結(jié)束狀態(tài)不執(zhí)行播放結(jié)束后重播會觸發(fā) seeking 和 seeked ,一般瀏覽器觸發(fā)一次逐沙, android 下uc瀏覽器觸發(fā)多次
解決:同上一些瀏覽器監(jiān)聽不到 seeking 和 seeked
解決:在 timeupdate 里來分析猜測用戶行為一些瀏覽器存在多次連續(xù)觸發(fā) seeking + seeked 的情況
解決:時間戳 + 節(jié)流 等待最后一次seeking 和 seeked 與 timeupdate 需要保證不會同時執(zhí)行
解決:監(jiān)聽到 seeking 觸發(fā)哲思,就不再執(zhí)行 timeupdate 模擬
走過的坑:我曾設想在播放時直接判斷出是否支持 seeking ,方式是播放時設置 currentTime 為 0.01 吩案,然后檢測 seeking 屬性棚赔,后來發(fā)現(xiàn)瀏覽器在這樣設置后的 seeking 屬性值不一致個別瀏覽器播放狀態(tài)下不觸發(fā) seeking 和 seeked ,但是在重播的時候觸發(fā)
解決:聲明狀態(tài)變量务热,隨著具體操作更新狀態(tài)忆嗜,結(jié)束狀態(tài)不監(jiān)聽 seeking 觸發(fā)
?
默認控件的隱藏
*::-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是全局函數(shù)崎岂,它可以接收三個參數(shù)(id,options,onready)第三個是回掉函數(shù)
- options
有兩種方式可以改變videojs的行為:
- 通過添加video標簽的data-setup屬性:
<video data-setup='{"autoplay":"true",.....}'
var player = videojs('example_video_1',{autoplay:true,....}) ,
直接傳入options
由于第一種方式是寫在html標簽中,通過JSON.parse解析闪湾,性能低冲甘,還容易報錯。個人更傾向于方法2.
這里有大量關(guān)于options的配置參數(shù):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進行控制了江醇。也就是說界面上不會出現(xiàn)任何控制按鈕
height: 300px
//視頻容器的高度,字符串或數(shù)字 單位像素 比如: height:300 or height:'300px'
width: 300px
//視頻容器的寬度, 字符串或數(shù)字 單位像素
loop : true/false //視頻播放結(jié)束后何暇,是否循環(huán)播放
muted : true/false //是否靜音
poster: 播放前顯示的視頻畫面陶夜,播放開始之后自動移除。通常傳入一個URL
preload:auto//預加載
auto //自動
metadata //元數(shù)據(jù)信息 裆站,比如視頻長度条辟,尺寸等
none //不預加載任何數(shù)據(jù),直到用戶開始播放才開始下載
children: Array | Object
//可選子組件 從基礎(chǔ)的Component組件繼承而來的子組件宏胯,數(shù)組中的順序?qū)⒂绊懡M件的創(chuàng)建順序哦羽嫡。
// 下面的方式只使用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 //使用哪種技術(shù)播放,可選值有'html5','flash' 默認為['html5'], 注意: 在v6.0.0 及以上的版本中肩袍,默認不包含flash的使用代碼杭棵。如果要使用flash播放的,需要手動引入flash相關(guān)邏輯的代碼氛赐。且需要指定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中獲取一個基礎(chǔ)組件
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方法用來實現(xiàn)繼承先舷,等同于ES6環(huán)境中的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.
// 這個構(gòu)造函數(shù)接收兩個參數(shù):
// player將被用來關(guān)聯(lián)options中的參數(shù)
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!
// 在做其它事之前先調(diào)用父類的構(gòu)造函數(shù)是很重要的,
// 這樣可以使父組件的所有特性在子組件中開箱即用滓侍。
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.
// 創(chuàng)建一個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內(nèi)置樣式約定俗成的做法
className: 'vjs-title-bar'
});
},
// This function could be called at any time to update the text
// contents of the component.
// 這個方法可以在任何需要更新這個組件內(nèi)容的時候調(diào)用
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上的總結(jié)以及一些demo
參考資料
原生video
videojs 文檔
videojs github
html5--移動端視頻video的android兼容,去除播放控件浇衬、全屏等
移動端手機網(wǎng)頁強制橫屏或全屏模仿橫評的js和css3方法
videojs 使用以及創(chuàng)建組件
videojs的使用 方法 事件 比較全
video.js--很贊的H5視頻播放庫
video.js 的文檔