Electron Screen API 知识点总结
目录
- [1. 概述](#1. 概述 "#1-%E6%A6%82%E8%BF%B0")
- [2. 核心概念](#2. 核心概念 "#2-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5")
- [3. 事件](#3. 事件 "#3-%E4%BA%8B%E4%BB%B6")
- [4. 方法](#4. 方法 "#4-%E6%96%B9%E6%B3%95")
- [5. Display 对象](#5. Display 对象 "#5-display-%E5%AF%B9%E8%B1%A1")
- [6. Point 和 Rectangle](#6. Point 和 Rectangle "#6-point-%E5%92%8C-rectangle")
- [7. 实际应用示例](#7. 实际应用示例 "#7-%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E7%A4%BA%E4%BE%8B")
- [8. 坐标系统](#8. 坐标系统 "#8-%E5%9D%90%E6%A0%87%E7%B3%BB%E7%BB%9F")
- [9. 平台差异](#9. 平台差异 "#9-%E5%B9%B3%E5%8F%B0%E5%B7%AE%E5%BC%82")
1. 概述
screen 模块作用
获取屏幕、显示器、鼠标位置等信息,用于:
- 创建填充屏幕的窗口
- 多显示器支持
- 监听屏幕变化
- 窗口定位
引入方式
javascript
复制代码
const { screen } = require('electron')
// ⚠️ 注意:在渲染进程中使用 window.screen 会冲突
// 应该使用 electron/main 的 screen
2. 核心概念
2.1 Display(显示器)
javascript
复制代码
{
id: number, // 显示器唯一标识
label: string, // 显示器标签
bounds: Rectangle, // 显示器的位置和大小
workArea: Rectangle, // 工作区域(排除任务栏等)
size: Size, // 显示器大小 { width, height }
workAreaSize: Size, // 工作区域大小
scaleFactor: number, // 缩放因子 (DPI)
rotation: number, // 旋转角度 (0, 90, 180, 270)
touchSupport: string, // 触摸支持: 'available' | 'none'
internal: boolean, // 是否为内置显示器
primary: boolean // 是否为主显示器
}
2.2 工作区域 vs 边界
scss
复制代码
┌────────────────────────────────────────────────────────┐
│ 边界 (bounds) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 工作区域 (workArea) │ │
│ │ (排除系统UI) │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ 可用区域 │ │ │
│ │ │ │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ████████████████████████████████████████████ │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
2.3 坐标系说明
| 类型 |
说明 |
用途 |
| 物理像素 |
实际硬件像素 |
精确屏幕坐标 |
| DIP 点 |
虚拟化像素(自动缩放) |
跨设备一致性 |
3. 事件
3.1 显示器连接/断开
javascript
复制代码
const { screen } = require('electron')
// 新显示器连接
screen.on('display-added', (event, newDisplay) => {
console.log('新显示器:', newDisplay.label)
console.log('分辨率:', newDisplay.bounds.width, 'x', newDisplay.bounds.height)
})
// 显示器断开
screen.on('display-removed', (event, oldDisplay) => {
console.log('显示器断开:', oldDisplay.label)
})
3.2 显示器配置变化
javascript
复制代码
screen.on('display-metrics-changed', (event, display, changedMetrics) => {
console.log('变化的属性:', changedMetrics)
// Possible values: 'bounds', 'workArea', 'scaleFactor', 'rotation'
if (changedMetrics.includes('bounds')) {
console.log('显示器位置或大小改变')
}
if (changedMetrics.includes('scaleFactor')) {
console.log('DPI 缩放因子改变')
}
})
4. 方法
4.1 获取鼠标位置
javascript
复制代码
const { screen } = require('electron')
const cursorPoint = screen.getCursorScreenPoint()
console.log('鼠标位置:', cursorPoint.x, cursorPoint.y)
// 返回 DIP 点
4.2 获取主显示器
javascript
复制代码
const primaryDisplay = screen.getPrimaryDisplay()
console.log('主显示器:')
console.log(' 分辨率:', primaryDisplay.size)
console.log(' 工作区:', primaryDisplay.workAreaSize)
console.log(' 缩放:', primaryDisplay.scaleFactor)
4.3 获取所有显示器
javascript
复制代码
const displays = screen.getAllDisplays()
displays.forEach((display, index) => {
console.log(`显示器 ${index + 1}:`)
console.log(' ID:', display.id)
console.log(' 标签:', display.label)
console.log(' 位置:', display.bounds.x, display.bounds.y)
console.log(' 大小:', display.bounds.width, 'x', display.bounds.height)
console.log(' 是否主显示器:', display.primary)
})
4.4 获取最近的显示器
javascript
复制代码
// 根据点获取最近的显示器
const point = { x: 1920, y: 1080 }
const nearestDisplay = screen.getDisplayNearestPoint(point)
// 根据矩形获取匹配的显示器
const rect = { x: 100, y: 100, width: 800, height: 600 }
const matchingDisplay = screen.getDisplayMatching(rect)
5. Display 对象
5.1 完整示例
javascript
复制代码
const { screen } = require('electron')
const display = screen.getPrimaryDisplay()
console.log('=== 显示器信息 ===')
console.log('ID:', display.id)
console.log('标签:', display.label)
console.log('是否主显示器:', display.primary)
console.log('是否内置:', display.internal)
console.log('\n边界 (bounds):')
console.log(' 位置: x=', display.bounds.x, 'y=', display.bounds.y)
console.log(' 大小: width=', display.bounds.width, 'height=', display.bounds.height)
console.log('\n工作区 (workArea):')
console.log(' 位置: x=', display.workArea.x, 'y=', display.workArea.y)
console.log(' 大小: width=', display.workArea.width, 'height=', display.workArea.height)
console.log('\n尺寸 (size):')
console.log(' width:', display.size.width)
console.log(' height:', display.size.height)
console.log('\n缩放因子:', display.scaleFactor)
console.log('旋转角度:', display.rotation)
console.log('触摸支持:', display.touchSupport)
6. Point 和 Rectangle
6.1 Point 结构
javascript
复制代码
{ x: number, y: number }
6.2 Rectangle 结构
javascript
复制代码
{ x: number, y: number, width: number, height: number }
6.3 坐标转换方法
| 方法 |
说明 |
平台 |
screen.screenToDipPoint(point) |
物理点 → DIP 点 |
Windows/Linux |
screen.dipToScreenPoint(point) |
DIP 点 → 物理点 |
Windows/Linux |
screen.screenToDipRect(window, rect) |
物理矩形 → DIP 矩形 |
Windows |
screen.dipToScreenRect(window, rect) |
DIP 矩形 → 物理矩形 |
Windows |
javascript
复制代码
// Windows/Linux 示例
const physicalPoint = { x: 3840, y: 2160 } // 4K 显示器
const dipPoint = screen.screenToDipPoint(physicalPoint)
console.log('DIP 点:', dipPoint)
// Windows 矩形转换
const physicalRect = { x: 0, y: 0, width: 2560, height: 1440 }
const dipRect = screen.screenToDipRect(null, physicalRect)
7. 实际应用示例
7.1 创建全屏窗口
javascript
复制代码
const { app, BrowserWindow, screen } = require('electron')
app.whenReady().then(() => {
const primaryDisplay = screen.getPrimaryDisplay()
const { width, height } = primaryDisplay.workAreaSize
const mainWindow = new BrowserWindow({
width,
height,
x: primaryDisplay.bounds.x,
y: primaryDisplay.bounds.y
})
mainWindow.loadURL('https://example.com')
})
7.2 在外接显示器创建窗口
javascript
复制代码
const { app, BrowserWindow, screen } = require('electron')
app.whenReady().then(() => {
const displays = screen.getAllDisplays()
// 找到外接显示器(位置不在原点)
const externalDisplay = displays.find(display => {
return display.bounds.x !== 0 || display.bounds.y !== 0
})
if (externalDisplay) {
const win = new BrowserWindow({
x: externalDisplay.bounds.x + 50,
y: externalDisplay.bounds.y + 50,
width: 800,
height: 600
})
win.loadURL('https://example.com')
}
})
7.3 创建居中窗口
javascript
复制代码
function createCenteredWindow() {
const primaryDisplay = screen.getPrimaryDisplay()
const { width: screenWidth, height: screenHeight } = primaryDisplay.workAreaSize
const windowWidth = 1024
const windowHeight = 768
const x = Math.round((screenWidth - windowWidth) / 2)
const y = Math.round((screenHeight - windowHeight) / 2)
return new BrowserWindow({
width: windowWidth,
height: windowHeight,
x,
y
})
}
7.4 监听显示器变化
javascript
复制代码
const { app, BrowserWindow, screen } = require('electron')
let mainWindow = null
function createWindow() {
mainWindow = new BrowserWindow({ ... })
}
app.whenReady().then(() => {
createWindow()
// 显示器配置改变时调整窗口
screen.on('display-metrics-changed', (event, display, changedMetrics) => {
if (mainWindow && display.id === screen.getPrimaryDisplay().id) {
if (changedMetrics.includes('workArea')) {
// 重新计算窗口位置
const { width, height } = display.workArea
mainWindow.setSize(Math.min(width, 1200), Math.min(height, 800))
mainWindow.center()
}
}
})
// 显示器连接/断开
screen.on('display-added', (event, newDisplay) => {
console.log('新显示器连接:', newDisplay.label)
// 可以自动在新显示器上创建窗口
})
screen.on('display-removed', (event, oldDisplay) => {
console.log('显示器断开:', oldDisplay.label)
// 检查是否有窗口在该显示器上,如果有则移动
})
})
7.5 多显示器管理器
javascript
复制代码
class MultiMonitorManager {
constructor() {
this.displays = screen.getAllDisplays()
this.windows = new Map()
// 监听变化
screen.on('display-added', (...args) => this.handleDisplayChange(...args))
screen.on('display-removed', (...args) => this.handleDisplayChange(...args))
screen.on('display-metrics-changed', (...args) => this.handleDisplayChange(...args))
}
handleDisplayChange() {
this.displays = screen.getAllDisplays()
console.log('当前显示器数量:', this.displays.length)
}
getAvailableDisplay() {
// 找到没有窗口占用的显示器
const usedDisplayIds = Array.from(this.windows.keys())
for (const display of this.displays) {
if (!usedDisplayIds.includes(display.id)) {
return display
}
}
// 如果都占用了,返回主显示器
return screen.getPrimaryDisplay()
}
moveWindowToDisplay(window, displayId) {
const display = this.displays.find(d => d.id === displayId)
if (display) {
window.setPosition(display.bounds.x, display.bounds.y)
window.setSize(display.workArea.width, display.workArea.height)
}
}
}
7.6 检测 HiDPI 显示器
javascript
复制代码
const { screen } = require('electron')
function isHiDPIDisplay() {
const primaryDisplay = screen.getPrimaryDisplay()
return primaryDisplay.scaleFactor > 1
}
function getDisplayQuality() {
const primaryDisplay = screen.getPrimaryDisplay()
const { scaleFactor } = primaryDisplay
if (scaleFactor <= 1) return '标准清晰度'
if (scaleFactor <= 1.5) return 'Retina (HD)'
if (scaleFactor <= 2) return 'Retina (FHD)'
return 'Retina (4K+)'
}
console.log('是否为高清屏:', isHiDPIDisplay())
console.log('显示质量:', getDisplayQuality())
8. 坐标系统
8.1 多显示器坐标
ini
复制代码
显示器 1 (主) 显示器 2 (外接)
┌────────────────┐ ┌────────────────┐
│ x=0, y=0 │ │ x=1920, y=0 │
│ │ │ │
│ ┌──────────┐ │ │ ┌──────────┐ │
│ │ 1920x1080 │ │ │ │ 1920x1080 │ │
│ └──────────┘ │ │ └──────────┘ │
└────────────────┘ └────────────────┘
8.2 坐标转换说明
javascript
复制代码
// 高 DPI 显示器 (4K)
// 物理像素: 3840 x 2160
// DIP 点: 1920 x 1080 (缩放因子 2x)
// 如果代码使用 DIP 坐标 (1920x1080)
// 系统会自动转换为物理像素 (3840x2160) 显示
9. 平台差异
9.1 功能支持
| 功能 |
Windows |
macOS |
Linux |
| 获取鼠标位置 |
✅ |
✅ |
✅ |
| 获取所有显示器 |
✅ |
✅ |
✅ |
| 主显示器 |
✅ |
✅ |
✅ |
| 触摸支持 |
✅ |
✅ |
✅ |
| 坐标转换 |
✅ |
❌ |
⚠️ |
| Wayland 支持 |
N/A |
N/A |
⚠️ |
9.2 Wayland 注意事项
scss
复制代码
⚠️ Linux Wayland 限制:
- screenToDipPoint() 不支持,会返回原值
- dipToScreenPoint() 不支持,会返回原值
- 需要使用 --ozone-platform=x11 参数启用 X11
9.3 Windows 坐标转换
javascript
复制代码
// Windows 支持完整的坐标转换
const point = { x: 100, y: 100 }
// 物理像素 → DIP 点
const dipPoint = screen.screenToDipPoint(point)
// DIP 点 → 物理像素
const physicalPoint = screen.dipToScreenPoint(dipPoint)
// 矩形转换
const rect = { x: 0, y: 0, width: 1920, height: 1080 }
const dipRect = screen.screenToDipRect(win, rect)
最佳实践
✅ 推荐做法
javascript
复制代码
// 1. 在 app ready 后使用
app.whenReady().then(() => {
const display = screen.getPrimaryDisplay()
// ...
})
// 2. 使用 workArea 而非 bounds
const { workAreaWidth, workAreaHeight } = screen.getPrimaryDisplay()
// 3. 监听显示器变化
screen.on('display-metrics-changed', (e, display, metrics) => {
if (metrics.includes('workArea')) {
// 调整窗口
}
})
// 4. 窗口居中显示
mainWindow.center()
❌ 避免做法
javascript
复制代码
// 1. 不要在 ready 前使用
// app.ready() 之前调用 screen 会报错
// 2. 不要硬编码分辨率
// 使用 screen API 获取实际屏幕大小
// 3. 渲染进程避免与 window.screen 冲突
// 使用 require('electron').screen 而不是 DOM 的 window.screen
方法速查表
| 方法 |
返回值 |
说明 |
getCursorScreenPoint() |
Point |
获取鼠标位置 |
getPrimaryDisplay() |
Display |
获取主显示器 |
getAllDisplays() |
Display[] |
获取所有显示器 |
getDisplayNearestPoint(point) |
Display |
获取最近的显示器 |
getDisplayMatching(rect) |
Display |
获取匹配的显示器 |
screenToDipPoint(point) |
Point |
物理→DIP (Win/Linux) |
dipToScreenPoint(point) |
Point |
DIP→物理 (Win/Linux) |
screenToDipRect(win, rect) |
Rectangle |
物理→DIP矩形 (Win) |
dipToScreenRect(win, rect) |
Rectangle |
DIP→物理矩形 (Win) |
事件速查表
| 事件 |
触发条件 |
参数 |
display-added |
新显示器连接 |
newDisplay |
display-removed |
显示器断开 |
oldDisplay |
display-metrics-changed |
显示器配置改变 |
display, changedMetrics[] |
文档基于 Electron v28+ Screen API 编写