記錄---前端實現畫中畫超簡單,讓網頁飛出瀏覽器

林恒發表於2024-12-02

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

Document Picture-in-Picture 介紹

今天,我來介紹一個非常酷的前端功能:文件畫中畫 (Document Picture-in-Picture, 本文簡稱 PiP)。你有沒有想過,網頁上的任何內容能懸浮在桌面上?😏

🎬 影片流媒體的畫中畫功能

你可能已經在影片平臺(如騰訊影片嗶哩嗶哩等網頁)見過這種效果:影片播放時,可以點選畫中畫後。無論你切換頁面,它都始終顯示在螢幕的最上層,非常適合上班偷偷看電視💻

在今天的教程中,不僅僅是影片,我將教你如何將任何 HTML 內容放入畫中畫模式,無論是動態內容、文字、圖片,還是純炫酷的 div,統統都能“飛”起來。✨

一個如此有趣的功能,在網上卻很少有詳細的教程來介紹這個功能的使用。於是我決定寫一篇詳細的教程來教大家如何實現畫中畫 (建議收藏)😁

體驗網址:Treasure-Navigation

📖 Document Picture-in-Picture 詳細教程

🛠 HTML 基本程式碼結構

首先,我們隨便寫一個簡單的 HTML 頁面,後續的 JS 和樣式都會基於它實現。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document Picture-in-Picture API 示例</title>
    <style>
        #pipContent {
            width: 600px;
            height: 300px;
            background: pink;
            font-size: 20px;
        }
    </style>
</head>

<body>
    <div id="container">
        <div id="pipContent">這是一個將要放入畫中畫的 div 元素!</div>
        <button id="clickBtn">切換畫中畫</button>
    </div>
    <script>
        // 在這裡寫你的 JavaScript 程式碼
    </script>
</body>
</html>

  

1️. 請求 PiP 視窗

PiP 的核心方法是 window.documentPictureInPicture.requestWindow。它是一個 非同步方法,返回一個新建立的 window 物件。
PIP 視窗可以將其看作一個新的網頁,但它始終懸浮在螢幕上方。

document.getElementById("clickBtn").addEventListener("click", async function () {
    // 獲取將要放入 PiP 視窗的 DOM 元素
    const pipContent = document.getElementById("pipContent");
    // 請求建立一個 PiP 視窗
    const pipWindow = await window.documentPictureInPicture.requestWindow({
        width: 200,  // 設定視窗的寬度
        height: 300  // 設定視窗的高度
    });

    // 將原始元素新增到 PiP 視窗中
    pipWindow.document.body.appendChild(pipContent);
});

演示:

👏 現在,我們已經成功建立了一個畫中畫視窗! 這段程式碼展示瞭如何將網頁中的元素放入一個新的畫中畫視窗,並讓它懸浮在最上面。非常簡單吧

關閉PIP視窗

可以直接點右上角關閉PIP視窗,如果我們想在程式碼中實現關閉,直接呼叫window上的api就可以了

window.documentPictureInPicture.window.close();

  

2️. 檢查是否支援 PiP 功能

一切不能相容瀏覽器的功能介紹都是耍流氓,我們需要檢查瀏覽器是否支援PIIP功能。 實際就是檢查documentPictureInPicture屬性是否存在於window上 🔧

if ('documentPictureInPicture' in window) {
    console.log("🚀 瀏覽器支援 PiP 功能!");
} else {
    console.warn("⚠️ 當前瀏覽器不支援 PiP 功能,更新瀏覽器或者換臺電腦吧!");
}

如果是隻需要將影片實現畫中畫功能,影片畫中畫 (Picture-in-Picture) 的相容性會好一點,但是它只能將元素放入畫中畫視窗。它與本文介紹的 文件畫中畫(Document Picture-in-Picture) 使用方法也是十分相似的。


3️. 設定 PiP 樣式

我們會發現剛剛建立的畫中畫沒有樣式,一點都不美觀。那是因為我們只放入了dom元素,沒有新增css樣式。

3.1. 全域性樣式同步

假設網頁中的所有樣式如下:

<head>
    <style>
        #pipContent {
            width: 600px;
            height: 300px;
            background: pink;
            font-size: 20px;
        }
    </style>
    <link rel="stylesheet" type="text/css" href="https://abc.css">
</head>

  為了方便,我們可以直接把之前的網頁的css樣式全部賦值給畫中畫

// 1. document.styleSheets獲取所有的css樣式資訊
[...document.styleSheets].forEach((styleSheet) => {
    try {
        // 轉成字串方便賦值
        const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join('');
        // 建立style標籤
        const style = document.createElement('style');
        // 設定為之前頁面中的css資訊
        style.textContent = cssRules;
        console.log('style', style);
        // 把style標籤放到畫中畫的<head><head/>標籤中
        pipWindow.document.head.appendChild(style);
    } catch (e) {
        // 透過 link 引入樣式,如果有跨域,訪問styleSheet.cssRules時會報錯。沒有跨域則不會報錯
        const link = document.createElement('link');
        /**
         * rel = stylesheet 匯入樣式表
         * type: 對應的格式
         * media: 媒體查詢(如 screen and (max-width: 600px))
         *  href: 外部樣式表的 URL
         */
        link.rel = 'stylesheet';
        link.type = styleSheet.type;
        link.media = styleSheet.media;
        link.href = styleSheet.href ?? '';
        console.log('error: link', link);
        pipWindow.document.head.appendChild(link);
    }
});

演示:

3.2. 使用 link 引入外部 CSS 檔案

向其他普通html檔案一樣,可以透過link標籤引入特定css檔案:

建立 pip.css 檔案:

#pipContent {
    width: 600px;
    height: 300px;
    background: skyblue;
}

js引用:

// 其他不變
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = './pip.css';  // 引入外部 CSS 檔案
pipWindow.document.head.appendChild(link);
pipWindow.document.body.appendChild(pipContent);
演示:

3.3. 媒體查詢的支援

可以設定媒體查詢 @media (display-mode: picture-in-picture)。在普通頁面中會自動忽略樣式,在畫中畫模式會自動渲染樣式

<style>
    #pipContent {
        width: 600px;
        height: 300px;
        background: pink;
        font-size: 20px;
    }
    
    <!-- 普通網頁中會忽略 -->
    @media (display-mode: picture-in-picture) {
        #pipContent {
            background: lightgreen;
        }
    }
</style>

  

在普通頁面中顯示為粉色,在畫中畫自動變為淺綠色

演示:

4️. 監聽進入和退出 PiP 模式的事件

我們還可以為 PiP 視窗 新增事件監聽,監控畫中畫模式的 進入 和 退出。這樣,你就可以在使用者操作時,做出相應的反饋,比如顯示提示或執行其他操作。

// 進入 PIP 事件
documentPictureInPicture.addEventListener("enter", (event) => {
    console.log("已進入 PIP 視窗");
});

const pipWindow = await window.documentPictureInPicture.requestWindow({
    width: 200,
    height: 300
});
// 退出 PIP 事件
pipWindow.addEventListener("pagehide", (event) => {
    console.log("已退出 PIP 視窗");
});

  

演示

5️. 監聽 PiP 焦點和失焦事件

const pipWindow = await window.documentPictureInPicture.requestWindow({
    width: 200,
    height: 300
});

pipWindow.addEventListener('focus', () => {
    console.log("PiP 視窗進入了焦點狀態");
});

pipWindow.addEventListener('blur', () => {
    console.log("PiP 視窗失去了焦點");
});

演示

6. 克隆節點畫中畫

我們會發現我們把原始元素傳入到PIP視窗後,原來視窗中的元素就不見了。
我們可以把原始元素克隆後再傳入給PIP視窗,這樣原始視窗中的元素就不會消失了

const pipContent = document.getElementById("pipContent");
const pipWindow = await window.documentPictureInPicture.requestWindow({
    width: 200,
    height: 300
});
// 核心程式碼:pipContent.cloneNode(true)
pipWindow.document.body.appendChild(pipContent.cloneNode(true));

演示

PIP 完整示例程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document Picture-in-Picture API 示例</title>
    <style>
        #pipContent {
            width: 600px;
            height: 300px;
            background: pink;
            font-size: 20px;
        }
    </style>
</head>
<body>
    <div id="container">
        <div id="pipContent">這是一個將要放入畫中畫的 div 元素!</div>
        <button id="clickBtn">切換畫中畫</button>
    </div>

    <script>
        // 檢查是否支援 PiP 功能
        if ('documentPictureInPicture' in window) {
            console.log("🚀 瀏覽器支援 PiP 功能!");
        } else {
            console.warn("⚠️ 當前瀏覽器不支援 PiP 功能,更新瀏覽器或者換臺電腦吧!");
        }

        // 請求 PiP 視窗
        document.getElementById("clickBtn").addEventListener("click", async function () {
            const pipContent = document.getElementById("pipContent");

            // 請求建立一個 PiP 視窗
            const pipWindow = await window.documentPictureInPicture.requestWindow({
                width: 200,  // 設定視窗的寬度
                height: 300  // 設定視窗的高度
            });

            // 將原始元素克隆並新增到 PiP 視窗中
            pipWindow.document.body.appendChild(pipContent.cloneNode(true));

            // 設定 PiP 樣式同步
            [...document.styleSheets].forEach((styleSheet) => {
                try {
                    const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join('');
                    const style = document.createElement('style');
                    style.textContent = cssRules;
                    pipWindow.document.head.appendChild(style);
                } catch (e) {
                    const link = document.createElement('link');
                    link.rel = 'stylesheet';
                    link.type = styleSheet.type;
                    link.media = styleSheet.media;
                    link.href = styleSheet.href ?? '';
                    pipWindow.document.head.appendChild(link);
                }
            });

            // 監聽進入和退出 PiP 模式的事件
            pipWindow.addEventListener("pagehide", (event) => {
                console.log("已退出 PIP 視窗");
            });

            pipWindow.addEventListener('focus', () => {
                console.log("PiP 視窗進入了焦點狀態");
            });

            pipWindow.addEventListener('blur', () => {
                console.log("PiP 視窗失去了焦點");
            });
        });

        // 關閉 PiP 視窗
        // pipWindow.close();  // 可以手動呼叫關閉視窗
    </script>
</body>
</html>

  

總結

🎉 你現在已經掌握瞭如何使用 Document Picture-in-Picture API 來懸浮任意 HTML 內容! 希望能帶來更靈活的互動體驗。✨

如果你有什麼問題,或者對 PiP 功能有更多的想法,歡迎在評論區與我討論!👇📬

本文轉載於:https://juejin.cn/post/7441954981342036006

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。

記錄---前端實現畫中畫超簡單,讓網頁飛出瀏覽器

相關文章