X5同層播放器應用實踐

heeroluo發表於1970-01-01

移動端瀏覽器中的video元素是比較特別的,早期無論是在iOS還是Android的瀏覽器中,它都位於頁面的最頂層,無法被遮擋。後來,這個問題在iOS下得到了解決。但是對Android的大部分瀏覽器來說,問題仍然存在。X5是騰訊基於Webkit開發的瀏覽器核心,應用於Android端的微信、QQ、QQ瀏覽器等應用。它提供了一種名叫「同層播放器」的特殊video元素以解決遮擋問題。

簡單使用

只要給普通的video元素加上X5的自定義屬性 x5-video-player-type ,就可以呼叫同層播放器。示例程式碼如下:

body {
    margin: 0;
    background: #000;
}
.video {
    width: 100%;
}
複製程式碼
<div class="player">
    <video id="video" class="video" controls="controls" playsinline x5-video-player-type="h5">
        <source src="video.mp4" />
    </video>
</div>
複製程式碼

點選播放後,頁面會瞬間拉伸(體驗有點差),然後就進入了全屏狀態,視訊預設在中間位置:

同層播放器預設UI

調整位置

在全屏狀態下,調整視訊位置的通用做法是:把video元素的尺寸設成滿屏,再通過 object-position 樣式屬性控制視訊內容的位置。相關程式碼如下:

.fullscreen .video {
    object-position: center top;
}
複製程式碼
var player = document.getElementById('video');
var isFullScreen;

// 進入全屏,設定狀態
player.addEventListener('x5videoenterfullscreen', function() {
    isFullScreen = true;
    // 在body上新增樣式類以控制全屏狀態下的頁面佈局
    document.body.classList.add('fullscreen');
}, false);

// 退出全屏時,清空狀態
player.addEventListener('x5videoexitfullscreen', function() {
    isFullScreen = false;
    document.body.classList.remove('fullscreen');
    player.style.width = player.style.height = '';
}, false);

// 同層播放器進入全屏狀態會導致視窗resize,但退出全屏不會
window.addEventListener('resize', function() {
    if (isFullScreen) {
        // 設為螢幕尺寸
        player.style.width = window.screen.width + 'px';
        player.style.height = window.screen.height + 'px';
    }
}, false);
複製程式碼

效果如下方左圖所示,可見,此時視訊距離頂部尚有一些距離。這個問題與 x5-video-player-fullscreen 屬性有關。如果不宣告這個屬性,原標題欄的佔位不會分配給頁面,而是平均分成上下兩塊,分別位於視窗頂部和底部。因此,視訊無法頂到最上方。

x5-video-player-fullscreen 的作用

補充 x5-video-player-fullscreen 屬性後,問題就解決了(上方右圖):

<video id="video" class="video" controls="controls" playsinline x5-video-player-type="h5" x5-video-player-fullscreen="true">
    <source src="video.mp4" />
</video>
複製程式碼

大家還可以發現,頂部有一層黑色漸變(上圖不太明顯,可以看下文的圖)以及兩個按鈕。據官方文件所述,這些都是無法移除的。

全屏狀態下的佈局

實際業務中,頁面多半不會只有一個視訊這麼簡單,下面就開始新增其他頁面元素(請行引入rem佈局所需的指令碼)。首先是在視訊之前加上標題欄:

.header {
    width: 100%;
    height: 1.14rem;
    line-height: 1.14rem;
    background: #fff;
    font-size: 0.36rem;
    text-align: center;
    color: #000;
}
複製程式碼
<header id="header" class="header">標題欄</header>
<div class="player">
    <video id="video" class="video" controls="controls" playsinline x5-video-player-type="h5" x5-video-player-fullscreen="true">
        <source src="video.mp4" />
    </video>
</div>
複製程式碼

然而,點選播放進入全屏狀態後,標題欄就消失了,其實它是被視訊擋住了。既然同層播放器是可以被遮擋的,那可以試試絕對定位:

.fullscreen .header {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 9999;
}
複製程式碼

從下方左圖可見,標題欄確實可以擋住視訊了。

標題欄

此時視訊內容被遮擋,所以要將其下移(上方右圖):

.fullscreen .video {
    object-position: center 1.14rem;
}
複製程式碼

接下來在視訊之後新增其他頁面元素,常規做法是限制視訊區域高度,再進行後面的佈局。但由於video元素本身在全屏狀態下的寬高必須設成滿屏,所以只能通過它的父元素(div.player)限制它的佔位:

.player {
    height: 4.22rem;
}
.fullscreen .player {
    /* 全屏狀態下重設高度,勿忘 */
    height: 5.36rem; /* 4.22 + 1.14 */
}
.video {
    width: 100%;
    height: 100%;
}
.main {
    box-sizing: border-box;
    padding: 0.3rem;
    height: 5rem;
    background: #fff;
}
複製程式碼
<header id="header" class="header">...</header>
<div class="player">...</div>
<div id="main" class="main">這裡是其他內容</div>
複製程式碼

而div.main本身是不需要做任何特殊處理的。然而,此時又出現了一個新問題:進入全屏狀態後,視訊控制欄不見了。原因是,video元素的高度為螢幕高度,控制欄位於螢幕最底端,而div.player又限制了高度,導致video元素超出的區域被隱藏,自然就看不到控制欄了。幸好,我們還可以通過偽元素選擇器修改控制欄的樣式:

.fullscreen .player {
    position: relative;
    height: 5.36rem;
}
.fullscreen .video::-webkit-media-controls {
    position: absolute;
    bottom: 0;
}
複製程式碼

通過使控制欄相對於div.player定位,就可以讓它回到視訊畫面的底端了。最終效果如下圖所示:

最終效果

綜上所述,在全屏狀態下,video元素之前的元素需要做佈局調整,而在其後的元素則不需要

頁面滾動

如果頁面有滾動條,進入全屏狀態後,是否還可以滾動呢?

筆者撰寫本文第一版(2017年中)的時候,全屏狀態下的頁面滾動會變成抖動,效果非常糟糕,而目前則是滾不了了。

所以,如果頁面內容確實較多,只能使用元素內滾動了

控制欄的坑

不得不說,原生控制欄的bug非常嚴重。

Bug 1:播放某些格式的視訊時,進度條會出現錯亂,即使退出全屏模式也還是錯亂。

進度條錯亂

Bug 2:控制欄的全屏按鈕在某些情況(具體規律尚未查明)下無效。

Bug 3:整個控制欄在某些情況(具體規律尚未查明)下無法調出。

以上三個bug非必現。但為了躲開這些坑,建議遮蔽原生控制欄,自行開發一個控制欄。

視訊全屏的實現

上一節提到,原生控制欄的全屏按鈕在某些情況下無效,這樣一來就必須想其他方法去實現視訊的全屏了,這裡面最關鍵就是video元素的 x5-video-orientation 屬性。它決定了同層播放器進入全屏狀態後,當前視窗是橫屏還是豎屏(前文的所有描述中,都是豎屏的情況)。並且,它是可以動態設定的。

如果把 x5-video-orientation 設成橫屏,再把頁面上的其他元素隱藏掉,就跟全屏無異了。具體實現如下:

var isLandscape;

// 點選其他內容區域,進入全屏
var main = document.getElementById('main');
main.addEventListener('click', function() {
    // 同層播放器進入全屏狀態之後,才能讓視訊全屏
    if (!isFullScreen) { return; }
    // 修改 x5-video-orientation
    player.setAttribute('x5-video-orientation', 'landscape');

    isLandscape = true;
}, false);

// 檢測視窗方向改變,修改佈局
window.addEventListener('orientationchange', function() {
    if (isLandscape) {
        document.body.classList.add('landscape');
        var width = window.screen.width;
        var height = window.screen.height;
        player.style.width = width + 'px';
        player.style.height = height + 'px';
        
    }
}, false);
複製程式碼
.landscape .header { display: none; }
.landscape .video { object-position: center center; }
.landscape .main { display: none; }
複製程式碼

要強調的是,從修改 x5-video-orientation 到視窗方向改變,需要一定的時間才能完成。因此,不要在修改之後馬上調整佈局,而是要監聽 window 的 orientationchange 事件,在事件回撥中調整佈局。此外,還要在退出全屏時,清空相關狀態:

player.addEventListener('x5videoexitfullscreen', function() {
    isFullScreen = false;
    isLandscape = false;
    document.body.classList.remove('fullscreen', 'landscape');
    player.style.width = player.style.height = '';
}, false);
複製程式碼

最終效果如下(頂部的陰影和兩個按鈕仍然無法幹掉):

視訊全屏效果

後記

本文第一版寫於2017年6月,當時剛接觸同層播放器,所以文章內容只能算是一份試用報告。經過一年多的專案實踐之後,有些舊問題找到了更好的解決方案,還發現並解決了一些新問題,而同層播放器本身也有一些變化,於是在2018年11月進行修訂,釋出第二版。

本文同時釋出於作者個人部落格: mrluo.life/article/det…

相關文章