Vue3 大屏项目投屏功能开发:多显示器适配实践

Vue3 大屏项目投屏功能开发:多显示器适配实践

近期开发大屏项目时,遇到一个特殊需求:在不同显示器上展示项目的不同页面。最初我对这个需求持怀疑态度,毕竟浏览器访问系统显示器参数并非常规操作。但查阅资料后发现,国外开发者已通过原生 HTML 实现类似功能,于是参考相关方案,将其适配到 Vue3 项目中,最终实现了多显示器投屏效果。

一、核心依赖:Window Management API

投屏功能的实现依赖浏览器的 Window Management API,该 API 能获取多屏幕详细信息、监听屏幕状态变化,还能控制窗口在指定屏幕的位置与尺寸。不过使用前需先确认浏览器兼容性,目前该 API 仅支持 Chrome 等现代浏览器,且要求项目运行在localhost或 HTTPS 环境下。

关键兼容性判断代码如下:

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

window.addEventListener('load', async () => {
  // 检查API支持情况
  if (!('getScreenDetails' in self) || !('isExtended' in screen) || !('onchange' in screen)) {
    ElMessage.error('当前浏览器版本不支持投屏功能')
    return
  }
  // 后续初始化逻辑...
})

二、核心逻辑:权限与屏幕信息管理

1. 权限申请与监听

使用 Window Management API 前,需获取窗口管理权限。通过navigator.permissions.query申请权限,并监听权限状态变化,确保功能正常使用:

javascript 复制代码
let permissionStatus = null // 存储权限状态
let screenDetails = null // 存储屏幕详情

// 初始化权限监听
const initPermission = async () => {
  permissionStatus = await navigator.permissions.query({ name: 'window-management' })
  // 监听权限变化
  permissionStatus.addEventListener('change', (e) => {
    permissionStatus = e
    updateScreens(false) // 权限变化后更新屏幕信息
  })
}

2. 屏幕信息获取与更新

通过getScreenDetails获取所有连接的显示器信息,包括分辨率、可用区域等,并监听屏幕状态变化(如新增 / 断开显示器、分辨率修改),实时更新屏幕列表:

javascript 复制代码
// 更新屏幕信息
const updateScreens = async (requestPermission = true) => {
  const screens = await getScreenDetailsWithFallback(requestPermission)
  return {
    screenList: screens,
    currentScreen: await getCurrentScreen() // 获取当前窗口所在屏幕
  }
}

// 获取屏幕详情(含兼容性兜底)
const getScreenDetailsWithFallback = async (requestPermission = false) => {
  if (!('getScreenDetails' in self)) return [window.screen]

  try {
    // 检查权限,无权限且允许申请时尝试获取
    if (!screenDetails && ((permissionStatus?.state === 'granted') || (permissionStatus?.state === 'prompt' && requestPermission))) {
      screenDetails = await window.getScreenDetails().catch(e => {
        console.error('获取屏幕信息失败', e)
        if (window.location.hostname !== 'localhost' && !window.location.protocol.includes('https')) {
          ElMessage.warning('投屏功能仅支持localhost或HTTPS环境')
        }
        return null
      })

      // 监听屏幕配置变化(如接入新显示器)
      if (screenDetails) {
        screenDetails.addEventListener('screenchange', () => {
          updateScreens(false)
          setScreenChangeListeners() // 绑定单个屏幕的变化监听
        })
        setScreenChangeListeners()
      }
    }

    // 提示单屏幕情况
    if (screenDetails?.screens.length === 1) {
      ElMessage.warning('请连接多个显示器以使用完整投屏功能')
    }

    return screenDetails?.screens || [window.screen]
  } catch (error) {
    console.error('处理屏幕信息出错', error)
    return [window.screen]
  }
}

// 绑定单个屏幕的变化监听
const setScreenChangeListeners = () => {
  const screens = screenDetails?.screens || [window.screen]
  screens.forEach(screen => {
    screen.onchange = () => updateScreens(false)
  })
}

3. 获取当前窗口所在屏幕

在多屏幕场景中,需明确当前窗口所在的显示器,避免重复打开或操作错误屏幕:

javascript 复制代码
const getCurrentScreen = async () => {
  if (!('getScreenDetails' in window)) return window.screen

  try {
    const screenDetails = await window.getScreenDetails()
    return screenDetails.currentScreen // 返回当前窗口所在屏幕
  } catch (error) {
    console.warn('获取当前屏幕信息失败', error)
    return window.screen
  }
}

三、功能实现:指定屏幕打开窗口

获取屏幕信息并确认权限后,即可在指定显示器上打开新窗口,实现投屏效果。通过window.open指定窗口位置与尺寸,确保新窗口在目标屏幕上全屏显示:

javascript 复制代码
let popup = null // 存储当前打开的窗口引用

const openPopup = async (screenIndex) => {
  const screens = await getScreenDetailsWithFallback(true)
  const currentScreen = await getCurrentScreen()

  // 检查目标屏幕是否存在
  const targetScreen = screens[screenIndex]
  if (!targetScreen) {
    ElMessage.error('无法获取指定屏幕信息,请检查环境或权限')
    return null
  }

  // 避免在当前屏幕重复打开
  if (currentScreen.label === targetScreen.label) {
    ElMessage.warning('当前屏幕已打开窗口,无需重复操作')
    return null
  }

  // 配置新窗口参数(全屏适配目标屏幕)
  const windowOptions = {
    url: window.origin, // 投屏页面地址(可替换为具体页面路径)
    x: targetScreen.availLeft || 0, // 窗口左上角x坐标(适配屏幕可用区域)
    y: targetScreen.availTop || 0, // 窗口左上角y坐标
    width: targetScreen.availWidth || 800, // 窗口宽度(屏幕可用宽度)
    height: targetScreen.availHeight || 600, // 窗口高度(屏幕可用高度)
    left: targetScreen.availLeft || 0, // 兼容部分浏览器的left参数
    top: targetScreen.availTop || 0 // 兼容部分浏览器的top参数
  }

  // 打开新窗口并存储引用
  popup = window.open(
    windowOptions.url,
    `screen-${screenIndex}`,
    `width=${windowOptions.width},height=${windowOptions.height},left=${windowOptions.left},top=${windowOptions.top}`
  )

  return popup
}

四、注意事项

  1. 环境限制:Window Management API 仅支持localhost或 HTTPS 环境,HTTP 环境下无法正常获取屏幕信息;
  2. 浏览器兼容性:目前仅 Chrome、Edge 等 Chromium 内核浏览器支持该 API, Firefox、Safari 暂不兼容;
  3. 权限管理:需用户主动授予窗口管理权限,否则无法获取多屏幕信息;
  4. 窗口监听:可添加定时器监听打开窗口的状态(如是否被关闭),避免内存泄漏或无效引用。

五、总结

Vue3 项目中的投屏功能,核心是借助 Window Management API 实现多屏幕信息的获取与窗口控制。通过权限管理、屏幕状态监听、指定窗口打开等逻辑,可实现不同显示器展示不同页面的需求。虽受限于浏览器兼容性与环境要求,但在大屏项目、多屏演示等场景中,仍能提供优质的功能体验。开发时需注意兼容性兜底与用户体验提示,确保功能稳定可用。

海云前端丨前端开发丨简历面试辅导丨求职陪跑

相关推荐
Mintopia4 分钟前
Web 安全与反编译源码下的权限设计:构筑前后端一致的防护体系
前端·安全
输出输入6 分钟前
前端核心技术
开发语言·前端
Mintopia11 分钟前
Web 安全与反编译源码下的权限设计:构建前后端一体的信任防线
前端·安全·编译原理
林深现海31 分钟前
Jetson Orin nano/nx刷机后无法打开chrome/firefox浏览器
前端·chrome·firefox
黄诂多1 小时前
APP原生与H5互调Bridge技术原理及基础使用
前端
前端市界1 小时前
用 React 手搓一个 3D 翻页书籍组件,呼吸海浪式翻页,交互体验带感!
前端·架构·github
文艺理科生1 小时前
Nginx 路径映射深度解析:从本地开发到生产交付的底层哲学
前端·后端·架构
千寻girling1 小时前
主管:”人家 Node 框架都用 Nest.js 了 , 你怎么还在用 Express ?“
前端·后端·面试
C澒1 小时前
Vue 项目渐进式迁移 React:组件库接入与跨框架协同技术方案
前端·vue.js·react.js·架构·系统架构
清山博客2 小时前
OpenCV 人脸识别和比对工具
前端·webpack·node.js