對於這個問題,其實網上已經有一些比較好的實踐,但有時候並不明白爲何這樣配置;若是你想要知道一個弱小,可憐又無助的
<video>
如何變成豐滿健壯的視頻播放器,那就往下看吧!css
來問H5 醫生端(PC & 微信)問題詳情頁面:html
<video>
標籤進行視頻播放src
屬性poster
屬性,值爲後端根據視頻第一幀生成的封面地址<video>
標籤使用 <div>
容器包裹,並設置 display: none
隱藏,用戶點擊封面時調用 video
的 play()
方法進行播放<video>
元素後微信中調用play()
無反應display: none;
或者 width: 0; height: 0;
方式隱藏視頻時,元素處於未激活狀態,不響應 play()
方法,因此咱們設置寬高爲1px
;play()
方法會觸發播放事件,但播放器此時並未打開,而全屏播放中暫停視頻並不會退出播放器。因此咱們能夠在視頻暫停時取消 loading);video
標籤處播放,致使有聲音無圖像;iPad 中經過修改 PC 的判斷條件能夠解決此時再使用原生 video 標籤事件和屬性,已經沒辦法進一步突破以上的這些坑,解決千差萬別的兼容性問題了。所以,咱們參考了其餘的方案實現了預期的效果。vue
PS:項目是基於Vue & scss,但該功能能夠不依賴這些實現web
webkit-playsinline, playsinline, x-webkit-airplay, x5-video-player-type, x5-video-player-fullscreen, x5-video-orientation
等解決不一樣類型設備的播放差別;<!-- 視頻容器 -->
<div class="video" :class="{'full-screen': isFullScreen}">
<!-- 視頻主體 -->
<div class="video-content">
<video :controls="isFullScreen" :style="isFullScreen ? {} : img.style" :class="img.isVertical ? 'vertical-img' : 'horizontal-img'" :src="img.url" preload="metadata" :poster="img.preview_pic_url" :ref="`video${img.id}`" webkit-playsinline="true" playsinline="true" x-webkit-airplay="allow" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint">
抱歉,您的瀏覽器不支持內嵌視頻!
</video>
</div>
<!-- 遮罩層,顯示播放按鈕;僅在待播放狀態顯示 -->
<div v-if="!isFullScreen" class="video-mask">
<div>
<img src="~images/play.png" />
</div>
</div>
<!-- 全屏控制按鈕;僅在非安卓高版本內核中顯示 -->
<div v-else-if="!inHighTBS" class="video-controls">
<span class="video-controls-close" @click.stop="handleVideoControls('close')">
×
</span>
</div>
</div>
複製代碼
屬性說明windows
controls
: 經過 flag 設置僅在播放時出現,避免初始播放狀態不一樣style
:對待播放 dom 進行絕對定位計算視頻偏移量;視頻顯示區爲正方形窗口,所以要橫向及縱向視頻顯示區都在正中間class
:設定橫向視頻高度或縱向視頻寬度src
:視頻源preload
:值爲預加載但不阻塞;每一個問題最多僅有一個視頻,保證用戶點擊播放後當即響應,而且不阻塞其餘圖片附件的渲染poster
:封面地址ref
:在vue
中獲取並操做 video
元素webkit-playsinline
:IOS 10中設置有效,視頻播放時局域播放,不脫離文檔流;能夠保證播放界面與PC端一致playsinline
:IOS 微信瀏覽器支持小窗內播放,和上一個屬性一塊兒食用可兼容幾乎全部IOS設備x5-video-player-type
:啓用H5同層播放器,是 wechat 安卓版特性x5-video-player-fullscreen
:視頻播放時將會進入到全屏模式,若不設置仍是會新開播放器,但尺寸爲原始視口大小(視頻未播放前)x5-video-orientation
:控制橫豎屏/* 外層還有其餘已定位容器 */
.video {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
transition: all 0.3s;
background-color: rgba(0, 0, 0, 0.5);
&.full-screen {
position: fixed;
z-index: 99;
.video-content {
width: 100%;
height: 100%;
video {
position: initial;
&.vertical-img {
height: 100%;
width: auto;
margin: 0 auto;
}
&.horizontal-img {
width: 100%;
height: auto;
max-height: 100%;
}
}
}
}
&-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
div {
width: 100%;
text-align: center;
img {
width: 30%;
position: inherit;
}
}
}
&-controls {
position: absolute;
right: 5%;
top: 5%;
display: flex;
align-items: center;
z-index: 1;
&-close {
width: 50px;
height: 50px;
line-height: 50px;
color: rgba(255, 255, 255, 0.7);
background: rgba(0, 0, 0, 0.3);
text-align: center;
border-radius: 50%;
font-size: 2rem;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.5);
color: #fff;
}
}
}
&-content {
max-width: 768px; // 限制PC端不超過768px;如要PC全屏可不作設置
margin: 0 auto;
display: flex;
align-items: center;
video {
display: block;
position: absolute;
object-fit: fill;
}
}
}
.horizontal-img {
height: 100%;
top: 0;
}
.vertical-img {
width: 100%;
left: 0;
}
複製代碼
在安卓微信中,就算加上了上面的屬性,還會出現上下有黑邊,不能全屏。解決給<video>
加上object-fit: fill;
的樣式便可。後端
修改上線後之前報Bug的用戶紛紛反饋好了,沒問題,但...瀏覽器
有一個用戶反饋使用錘子的堅果Pro點擊視頻無反應,找來同型號測試機一番騷操做後...愣是沒復現!緩存
以後經過和用戶不斷溝通發現該設備上竟然未使用X5內核瀏覽器(使用微信打開debugtbs.qq.com
可調試X5內核,未安裝會有提示)微信
所以在下面一個安卓兼容性事件判斷上報錯了,使用try { } catch (e) {}
包一下,一樣能夠正常播放,但這是的播放效果已沒法統一。dom
// 高版本微信安卓環境下會自動加上返回按鈕而且點擊觸發退出全屏事件
// 需作未使用X5內核容錯處理
inHighTBS() {
if (inAndroid) {
try {
const [, currentTbsVersion] = window.navigator.userAgent.match(/TBS\/(\d+)/);
return currentTbsVersion > '036900';
} catch() {
return false;
}
} else {
return false;
}
}
// 安卓環境中會啓用同層H5播放器,跳轉新窗口,所以監聽x5videoexitfullscreen事件可獲取狀態
// https://x5.tencent.com/tbs/guide/video.html
this.inHighTBS && vDom.addEventListener('x5videoexitfullscreen', () => {
this.isFullScreen = false;
});
複製代碼
const mediaProperties = [
'loadstart', // 在媒體開始加載時觸發。
'progress', // 告知媒體相關部分的下載進度時週期性地觸發。有關媒體當前已下載總計的信息能夠在元素的buffered屬性中獲取到。
'suspend', // 在媒體資源加載終止時觸發,這多是由於下載已完成或由於其餘緣由暫停。
'abort', // 在播放被終止時觸發,例如, 當播放中的視頻從新開始播放時會觸發這個事件。
'error', // 在發生錯誤時觸發。元素的error屬性會包含更多信息。參閱Error handling得到詳細信息。
'emptied', // 媒體被清空(初始化)時觸發。
'stalled', // 在嘗試獲取媒體數據,但數據不可用時觸發。
'loadedmetadata', // 媒體的元數據已經加載完畢,如今全部的屬性包含了它們應有的有效信息。
'loadeddata', // 媒體的第一幀已經加載完畢。
'canplay', // 在媒體數據已經有足夠的數據(至少播放數幀)可供播放時觸發。這個事件對應CAN_PLAY的readyState。
'canplaythrough', // 在媒體的readyState變爲CAN_PLAY_THROUGH時觸發,代表媒體能夠在保持當前的下載速度的狀況下不被中斷地播放完畢。注意:手動設置currentTime會使得firefox觸發一次canplaythrough事件,其餘瀏覽器或許不會如此。
'playing', // 在媒體開始播放時觸發(不管是初次播放、在暫停後恢復、或是在結束後從新開始)。
'waiting', // 在一個待執行的操做(如回放)因等待另外一個操做(如跳躍或下載)被延遲時觸發。
'seeking', // 在跳躍操做開始時觸發。
'seeked', // 在跳躍操做完成時觸發。
'ended', // 播放結束時觸發。
'durationchange', // 元信息已載入或已改變,代表媒體的長度發生了改變。例如,在媒體已被加載足夠的長度從而得知總長度時會觸發這個事件。
'timeupdate', // 元素的currentTime屬性表示的時間已經改變。
'play', // 在媒體回放被暫停後再次開始時觸發。即,在一次暫停事件後恢復媒體回放。
'pause', // 播放暫停時觸發。
'ratechange', // 在回放速率變化時觸發。
'resize',
'volumechange', // 在音頻音量改變時觸發(既能夠是volume屬性改變,也能夠是muted屬性改變).。
'mozaudioavailable' // 當音頻數據緩存並交給音頻層處理時
];
mediaProperties.forEach(item => {
vDom.addEventListener(item, e => console.log(item));
});
複製代碼
做者:丁香醫生團隊 顧重