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

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

相关推荐
mldong6 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排6 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60617 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角8 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!8 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰8 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi10 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen10 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马11 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒11 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端