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在指定的显示器新开一个窗口

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

相关推荐
excel3 分钟前
webpack 模块图 第 五 节
前端
好_快9 分钟前
Lodash源码阅读-baseIndexOfWith
前端·javascript·源码阅读
好_快10 分钟前
Lodash源码阅读-basePullAll
前端·javascript·源码阅读
excel13 分钟前
webpack 模块图 第 四 节
前端
好_快15 分钟前
Lodash源码阅读-baseUnary
前端·javascript·源码阅读
好_快15 分钟前
Lodash源码阅读-pullAll
前端·javascript·源码阅读
洛小豆15 分钟前
Vue Router 中的 Hash 模式与 History 模式
前端·javascript·vue.js
珹洺1 小时前
JSP技术入门指南【一】利用IDEA从零开始搭建你的第一个JSP系统
java·开发语言·前端·html·intellij-idea·jsp
2401_878454534 小时前
Themeleaf复用功能
前端·学习