Vue3项目投屏功能开发

最近接了个大屏项目,产品想在不同的显示器上展示大屏项目不同的页面,做出来的效果图大概长这样 看着这个图的第一时间我就去找产品掰扯,想让他把这个功能pass掉。。。我在想浏览器怎么访问到系统的参数。 当我去翻阅文献的时候发现国外有大佬做出来了这个功能而且很全面。但是使用原生的html写的。我决定参考他的demo把他搬到大屏的项目里头。

参考文献:developer.chrome.com/docs/capabi...

参考demo:michaelwasserman.github.io/window-plac...

不多bb直接看关键代码:

js 复制代码
import { ElMessage } from 'element-plus'

let permissionStatus = null; // 存储窗口管理权限状态
let screenDetails = null; // 屏幕详情信息
let popup = null; // 存储当前打开的窗口引用
let popupObserverInterval = null; // 存储监视窗口状态的定时器ID

window.addEventListener('load', async () => {
    // getScreenDetails: 获取多屏幕详细信息、isExtended: 检查是否是扩展显示、onchange: 屏幕变化事件支持
    if (!('getScreenDetails' in self) || !('isExtended' in screen) || !('onchange' in screen)) {
        // ElMessage.error('浏览器版本不支持!')
    } else {
        // 监听屏幕变化(新增显示器,断开显示器)
        screen.addEventListener('change', () => {
            updateScreens(/*requestPermission=*/false)
        })

        // 窗口变化
        window.addEventListener('resize', () => {
            updateScreens(/*requestPermission=*/false)
        })

        // 查看窗口管理权限
        /**
         * window.navigator 是 JavaScript 中的一个对象,它提供了有关浏览器和操作系统的一些信息。
         * 
         * 使用方法:navigator.xxxxx
         * 
         * 主要功能:获取浏览器信息、设备能力检测、硬件访问、位置服务、多媒体和设备接口
         * 
         * 浏览器信息:
         * userAgent:获取浏览器以及操作系统的信息
         * appName:获取浏览器名称
         * appVersion:获取浏览器版本
         * platform:获取操作系统类型
         * 
         * 设备能力检测:
         * cookieEnabled:浏览器是否启用cookie
         * onLine:浏览器是否在线
         * language/languages:用户首选浏览器语言
         * 
         * 硬件访问:
         * hardwareConcurrency:可用CPU数量
         * deviceMemory:设备内存(GB)
         *  
         * 位置服务:
         * geolocation:获取用户位置信息
         * 
         * 多媒体和设备接口:
         * mediaDevices 用于获取用户的媒体设备(如摄像头、麦克风等)。
         */
        permissionStatus = await navigator.permissions.query({ name: 'window-management' })

        // 监听窗口权限变化
        permissionStatus.addEventListener('change', (e) => {
            permissionStatus = e
            updateScreens(/*requestPermission=*/false)
        })
    }
    updateScreens(/*requestPermission=*/false)
})

// 更新屏幕信息
const updateScreens = async (requestPermission = true) => {
    const screens = await getScreenDetailsWithWarningAndFallback(requestPermission)
    return {
        screenList: screens,
        currentScreen: await getCurrentScreen()
    }
}

// 获取所有连接的显示器信息
const getScreenDetailsWithWarningAndFallback = async (requestPermission = false) => {
    if ('getScreenDetails' in self) {
        try {
            // 检查和请求权限
            if (!screenDetails && ((permissionStatus && permissionStatus.state == 'granted') || (permissionStatus && permissionStatus.state == 'prompt' && requestPermission))) {
                // getScreenDetails:Window Management API 的方法 允许网页应用获取关于用户显示器配置的详细信息。
                screenDetails = await getScreenDetails().catch(e => { 
                    console.error('获取屏幕详情失败:', e); 
                    // 检查是否是由于非localhost环境引起的错误
                    if (window.location.hostname !== 'localhost' && !window.location.protocol.includes('https')) {
                        ElMessage.warning('Window Management API仅在localhost或HTTPS环境下可用');
                    }
                    return null;
                });
                // 监听屏幕配置变化(如接入新显示器、分辨率改变等)
                if (screenDetails) {
                    screenDetails.addEventListener('screenchange', () => {
                        updateScreens(/*requestPermission=*/false)
                        setScreenListeners()
                    })
                    setScreenListeners()
                }
            }

            // 抛错
            if (screenDetails && screenDetails.screens.length > 1) {
            } else if (screenDetails && screenDetails.screens.length == 1) {
                // ElMessage.error('请扩展您的桌面到多个屏幕以获得完整的演示功能')
            } else if (screenDetails || (permissionStatus && permissionStatus.state === 'denied')) {
                ElMessage.error('请允许窗口管理权限以获得完整的演示功能')
            }

            if (screenDetails) {
                return screenDetails.screens
            }
        } catch (error) {
            console.error('获取屏幕详情过程中出错:', error);
        }
    } else {
        console.warn('当前环境不支持Window Management API');
    }
    // 如果不支持多屏幕API,返回基本的screen信息
    return [window.screen]
}

// 设置屏幕变化的监听器
const setScreenListeners = () => {
    let screens = screenDetails ? screenDetails.screens : [window.screen]
    for (const s of screens) {
        s.onchange = () => { updateScreens(/*requestPermission=*/false) }
    }
}

// 获取窗口所在显示器
const getCurrentScreen = async () => {
    try {
        // Check if getScreenDetails is available
        if (!('getScreenDetails' in window)) {
            console.warn('getScreenDetails API is not available in this context.');
            return window.screen; // fallback to basic screen info
        }
        
        const screenDetails = await window.getScreenDetails();
        const currentScreen = screenDetails.currentScreen;
        return currentScreen;
    } catch (error) {
        console.warn('Failed to get screen details:', error);
        return window.screen; // fallback to basic screen info
    }
}

/**
 * 在指定屏幕打开新浏览器窗口前的设置
 * @param screenIndex 屏幕索引
 * */
const openPopup = async (screenIndex) => {
    try {
        const screens = await getScreenDetailsWithWarningAndFallback(/*requestPermission=*/true);
        const currentScreen = await getCurrentScreen();

        if (screenIndex == 0 || screenIndex) {
            // if (currentScreen.label == screens[screenIndex].label) {
            //     ElMessage.error('当前屏幕已打开')
            //     return
            // }

            // 选择需要打开的屏幕
            const screen = screens[screenIndex];
            
            if (!screen) {
                ElMessage.error('无法获取指定屏幕信息,可能是在非HTTPS或非localhost环境');
                return null;
            }

            // 新开浏览器窗口设置
            const option = {
                url: window.origin, // 跳转地址
                x: screen.availLeft || 0, // 屏幕可用区域的左边界
                y: screen.availTop || 0, // 屏幕可用区域的上边界
                width: screen.availWidth || 800, // 屏幕可用区域的宽
                height: screen.availHeight || 600 // 屏幕可用区域的高
            }

            return openWindow(option);
        } else {
            ElMessage.warning('请选择需要投屏的屏幕!');
        }
    } catch (error) {
        console.error('打开窗口失败:', error);
        ElMessage.error('打开窗口失败,可能是在非HTTPS或非localhost环境');
        return null;
    }
}

// 打开浏览器窗口
const openWindow = (option) => {
    if (popupObserverInterval) clearInterval(popupObserverInterval)

    const features = getFeaturesFromOption(option)
    popup = window.open(option.url, '_blank', features)

    // 每300ms检查一次窗口是否关闭
    if (popup) {
        popupObserverInterval = setInterval(() => {
            // 窗口关闭时清除
            clearInterval(popupObserverInterval)
            popupObserverInterval = null
            popup = null
        }, 300)
    }

    return popup
}

// 窗口特性字符串处理
const getFeaturesFromOption = (option) => {
    return `left=${option.x},top=${option.y},width=${option.width},height=${option.height}`
}

export {
    updateScreens, openPopup
}

vue组件代码就很简单了就不展示了 调用也就是updateScreens方法检测显示器,openPopup在指定的显示器新开一个窗口

如果有侵权请私信我,直接删文处理了

相关推荐
河畔一角6 分钟前
一些感悟
前端
excel12 分钟前
理解 JavaScript 中的 for...in 与 for...of 的区别
前端
前端小巷子41 分钟前
Webpack 5模块联邦
前端·javascript·面试
玲小珑44 分钟前
Next.js 教程系列(十九)图像优化:next/image 与高级技巧
前端·next.js
晓得迷路了1 小时前
栗子前端技术周刊第 91 期 - 新版 React Compiler 文档、2025 HTML 状态调查、Bun v1.2.19...
前端·javascript·react.js
江城开朗的豌豆1 小时前
Vue和React中的key:为什么列表渲染必须加这玩意儿?
前端·vue.js·面试
江城开朗的豌豆1 小时前
前端路由傻傻分不清?route和router的区别,看完这篇别再搞混了!
前端·javascript·vue.js
pengzhuofan1 小时前
Web开发系列-第0章 Web介绍
前端
小鱼人爱编程1 小时前
Java基石--反射让你直捣黄龙
前端·spring boot·后端
JosieBook3 小时前
【web应用】如何进行前后端调试Debug? + 前端JavaScript调试Debug?
前端·chrome·debug