如何應對Chrome 112版本中的getDisplayMedia問題?
前言
前幾天chrome發布了112版本,原本可以通過getDisplayMedia正確的獲取當前標簽頁的內容,在此版本中卻出現了內容擠壓、概率性出現滾動條問題。
經過一番思考后,我想到了兩個處理方案,本文就跟大家分享下我的思路,歡迎各位感興趣的開發者閱讀本文。
定位問題
當我在瀏覽器中調試修復截圖插件的其他bug時,發現截取的內容出現了滾動條,這讓我感到非常詫異。難道是我在解決某個問題的同時引發了更多問題。于是,我嘗試回退代碼版本,但問題仍然存在,這使我陷入了沉思,開始求助別人、在搜索引擎尋找解決方案。
經過一番折騰后,我沒有得到任何答案。就在這時,有一個開發者在給我提了issue,他也遇到了同樣的問題。
他提供了瀏覽器版本號,這讓我恍然大悟,不久前我升級了瀏覽器版本并且版本號跟他的一樣,同為112版本。
為了驗證是否為瀏覽器升級所帶來的bug,我在虛擬機中安裝了110版本的chrome,發現這個問題確實不存在。難不成是Google改了API?我帶著這個疑問翻閱了getDisplayMedia的API文檔,并沒有發現有什么特殊說明。
事情陷入了僵局,我又陷入了沉思。突然間,我想起來之前有個網友說Google自家也有產品用到了這個API,找他要來了網址。
果然,他們產品也出現了問題。
截取后的內容出現了擠壓和滾動條
解決方案
Chrome在下個版本的更新中可能會解決此問題,也有可能不會解決。這是個未知數,我還是得想想其他辦法來處理處理這個問題。
隱藏滾動條并填充擠壓區域
最簡單粗暴的辦法就是在調用getDisplayMedia方法之前將頁面的寬、高分別設為100vw、100vh,給html和body元素設置overflow:hidden。部分代碼如下所示:
// 設置頁面寬高并隱藏滾動條
document.documentElement.classList.add("hidden-screen-shot-scroll");
document.body.classList.add("hidden-screen-shot-scroll");
.hidden-screen-shot-scroll {
width: 100vw;
height: 100vh;
overflow: hidden;
}
這樣設置后,又出現了新的問題,因為截圖容器的高度為body區域未擠壓時的正確高度,webrtc的共享彈窗會把頁面內容整體擠下去。
getDisplayMedia的回調里拿到的屏幕流數據是擠壓后的,底部會缺少一部分,這部分內容正好是共享彈窗的高度。當用戶想截取這部分的內容時,會發現拿到的是透明的。
為了解決這個問題,我想到了將這一部分用其他顏色填充,告訴用戶這部分不是截圖內容,部分代碼如下所示:
if (
this.hiddenScrollBar.state &&
diffHeight > 0 &&
this.hiddenScrollBar.fillState
) {
// 填充容器的剩余部分
imgContext.beginPath();
let fillWidth = containerWidth;
let fillHeight = diffHeight;
if (this.hiddenScrollBar.fillWidth > 0) {
fillWidth = this.hiddenScrollBar.fillWidth;
}
if (this.hiddenScrollBar.fillHeight > 0) {
fillHeight = this.hiddenScrollBar.fillHeight;
}
imgContext.rect(0, fixHeight, fillWidth, fillHeight);
imgContext.fillStyle = this.hiddenScrollBar.color;
imgContext.fill();
}
使用窗口截圖模式
通過上個章節的分析,我們發現使用標簽頁截圖時出現擠壓的根本原因在于共享彈窗會出現在頁面的頂部。那么,有沒有什么辦法能做到不讓這個共享彈窗出現在最頂部呢?
帶著這個疑問,我在chrome的開發文檔中找到了displaySurface選項,將這個值設為window,它的共享彈窗就不會出現了。部分代碼如下所示:
let mediaWidth = window.screen.width * this.dpr;
let mediaHeight = window.screen.height * this.dpr;
navigator.mediaDevices.getDisplayMedia({
audio: false,
video: {
width: mediaWidth,
height: mediaHeight,
displaySurface: "window"
}
});
網頁標題欄會出現錄制圖標
但是,這樣子做我們拿到的是整個瀏覽器窗口的截圖,我們想要的是body區域的截圖內容。 因此,還需要對截圖內容做進一步的裁剪處理,我們可以知道瀏覽器窗口的寬高、body區域的寬高。那么,用瀏覽器窗口的寬高減去body區域的寬高,我們就拿到了body區域的裁剪坐標。部分代碼如下所示:
/**
* 從窗口數據流中截取頁面body內容
* @param videoWidth 窗口寬度
* @param videoHeight 窗口高度
* @param containerWidth body內容寬度
* @param containerHeight body內容高度
* @private
*/
private getWindowContentData(
videoWidth: number,
videoHeight: number,
containerWidth: number,
containerHeight: number
) {
const videoCanvas = document.createElement("canvas");
videoCanvas.width = videoWidth;
videoCanvas.height = videoHeight;
const videoContext = getCanvas2dCtx(videoCanvas, videoWidth, videoHeight);
if (videoContext) {
videoContext.drawImage(this.videoController, 0, 0);
const startX = 0;
const startY = videoHeight - containerHeight;
const width = containerWidth;
const height = videoHeight - startY;
// 獲取裁剪框區域圖片信息;
return videoContext.getImageData(
startX * this.dpr,
startY * this.dpr,
width * this.dpr,
height * this.dpr
);
}
return null;
}
思考與分析
使用當前標簽頁進行截圖相對而言用戶體驗是最好的,但是因為chrome 112版本的bug會造成頁面內容擠壓導致截取到的內容不完整,因此只能采用其他方案來解決此問題了。窗口截圖模式和隱藏滾動條都可以解決這個問題,但是他們都有缺點:
- 窗口截圖模式可以完美的獲取屏幕截圖,但是用戶授權時會出現其他的應用程序選項,用戶體驗會差一些。
- 隱藏滾動條方案還是采用標簽頁截圖,但是會造成內容擠壓,底部出現空白。
這兩種方案都是不完美的,希望Chrome能在后續的版本更新中修復此問題。窗口模式截圖我有找過能否讓授權列表中只包含當前窗口選項,我在Chrome的開發文檔中找到了Google對此情況的解釋。
項目地址
本文所列舉的代碼完整內容請移步:
- hiddenScrollBar-main.ts
- wrcWindowMode-main.ts
- getWindowContentData-main.ts