Appearance
常见问题
苹果安装失败
如果软件执行时出现 xxx已损坏,无法打开。 你应该将它移到废纸篓
- 在命令行执行以下代码
- /Applications/camera.app 参数是从访达>应用程序 中找到应用图标,然后托到命令行即可
xattr -cr /Applications/camera.app
浏览器打开窗口
下面代码监听渲染进程中请求创建一个新窗口,然后使用系统的默认浏览器打开
//渲染进程中请求创建一个新窗口之前被调用
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
//取消新窗口创建
return { action: 'deny' }
})
苹果dock图标
下面主进程代码用于隐藏dock栏图标
app.whenReady().then(() => {
...
//隐藏苹果dock图标
if (process.platform == 'darwin') app.dock.hide()
//托盘
createTray()
})
任务栏图标
下面是主进程代码,用于定义任务栏图标

import { Menu, shell, Tray } from 'electron'
import path from 'path'
const createTray = () => {
const tray = new Tray(
path.resolve(
__dirname,
process.platform == 'darwin'
? '../../resources/trayTemplate@2x.png'
: '../../resources/windowTray.png'
)
)
const contextMenu = Menu.buildFromTemplate([
{ label: '退出程序', role: 'quit' },
{ type: 'separator' },
{ label: '问题反馈', click: () => shell.openExternal('https://www.banmashou.com') }
])
tray.setToolTip('斑马兽摄像头')
tray.setContextMenu(contextMenu)
}
export { createTray }
窗口托动
当把为窗口属性设置无边框时 frame:false,因为没有了标题栏窗口不能托动。
使用css 控制
new BrowserWindow({
title: 'Main window',
width: 414,
frame: false,
...
})
通过定义下面的Electron CSS样式,可以实现托动任何DOM都可以托动窗口。
html {
-webkit-app-region: drag;
}
定义了上面的样式后,如果页面中存在 textarea 标签时将不能实现托动放大,能过定义以下样式可以解决这个问题
textarea {
-webkit-app-region: no-drag;
}
鼠标在屏幕中的位置
//获取鼠标在屏幕的 x,y 坐标,根据电脑显示器排列方式,如果主显示器在右边,鼠标在左边显示器,则 x 为负数
const point = screen.getCursorScreenPoint()
鼠标所在的屏幕
const point = screen.getCursorScreenPoint()
//根据鼠标的 x、y 获取当前屏幕
const display = screen.getDisplayNearestPoint(point)
鼠标事件
当给某个子元素设置了 -webkit-app-region: drag 时,JS事件机制对该元素无效。所以会出现,当父元素使用 mouseenter 事件时,离开这个子元素到父元素时,会触发父元素的 mouseenter 事件。
使用 JS控制
下面是示例代码,useDrag.ts 是渲染进程接收 DOM 事件,然后调用主进程改变窗口位置
class Drag {
private body: HTMLBodyElement | null = null
constructor(private resPageX = 0, private resPageY = 0) {}
public run() {
window.addEventListener('DOMContentLoaded', () => {
this.body = document.querySelector('body')!
this.body.addEventListener('mousedown', this.down.bind(this))
})
}
private down(e: MouseEvent) {
this.resPageX = e.pageX
this.resPageY = e.pageY
const mouseEvent = this.move.bind(this)
this.body!.addEventListener('mousemove', mouseEvent)
this.body!.addEventListener('mouseup', () =>
this.body?.removeEventListener('mousemove', mouseEvent)
)
}
private move(e: MouseEvent) {
const x = e.pageX - this.resPageX
const y = e.pageY - this.resPageY
window.api.drag({ x, y })
}
}
export default () => {
const drag = new Drag()
return { drag }
}
然后是 preload.ts
contextBridge.exposeInMainWorld('api', {
drag: (opt: { x: number; y: number }) => {
ipcRenderer.invoke('drag', opt)
}
})
主进程 main.ts
import { BrowserWindow, ipcMain } from 'electron'
export default (win: BrowserWindow) => {
ipcMain.handle('drag', (_event, opt: { x: number; y: number }) => {
const [x, y] = win.getPosition()
win.setPosition(x + opt.x, y + opt.y)
})
}
无边框圆角
下面介绍无边框圆角窗口的定义,但是由于不同系统的限制,苹果系统表现良好,有些系统默认不会有效果。

index.html 模板
<main>
<video></video>
</main>
css 样式定义
main {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
border: solid 5px #9b59b6;
border-radius: 50%;
overflow: hidden;
video {
object-fit: cover;
}
}
main.js 主进程窗口定义
const mainWindow = new BrowserWindow({
width: 300,
height: 300,
minWidth: 200,
minHeight: 200,
frame:false,
transparent: true,
alwaysOnTop: true,
...
})
隐藏鼠标
主要是通过 webContents.insertCSS
添加样式来完成,下面是定义主进程事件,当切换全屏时对 video
标签隐藏鼠标
let cssKey = ''
ipcMain.on('toggleFullScreen', async (event: IpcMainEvent) => {
const win = BrowserWindow.fromWebContents(event.sender)!
if (win.isFullScreen()) {
win.webContents.removeInsertedCSS(cssKey)
win.setFullScreen(false)
cssKey = ''
} else {
cssKey = await win.webContents.insertCSS(
'video { cursor: none !important;-webkit-app-region: no-drag }',
)
win.setFullScreen(true)
}
})
Sqlite
启动软件时报以下错误
App threw an error during load
SqliteError: unable to open database file
at new Database (/Users/bm/code/tool/node_modules/.pnpm/better-sqlite3@11.1.1/node_modules/better-sqlite3/lib/database.js:69:26)
一般情况下是你启动了多次应用,造成 sqlite 数据库被使用,所以退出同名应用,只启动一个,这种情况一般出现在开发项目时。
编译后打开新窗口
有这个场景,我们想在打开新窗口时,显示 react 或 vue 的指定路由,但是编译后加载的是静态的 html 文件,不能直接操作。我们可以使用 node:url 模块来加载文件并指定 hash,解决这个问题。
import url from 'node:url'
...
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/#config')
} else {
mainWindow.loadURL(
url.format({
//编译后的文件
pathname: join(__dirname, '../renderer/index.html'),
//协议
protocol: 'file',
//protocol 后面需要两个/
slashes: true,
//hash 的值
hash: 'config'
})
)
}
鼠标穿透
思路就是鼠标进入 body 时让鼠标可以穿透到 electron 软件的后面程序,鼠标离开 body 元素时让鼠标不穿透,可以点击我们的 electron 软件。
创建hook 用于定义鼠标穿透事件
//鼠标穿透处理
export const useIgnoreMouseEvents = () => {
document.body?.addEventListener('mouseover', (e: MouseEvent) => {
if (e.target === document.body) {
window.api.setIgnoreMouseEvents(true, { forward: true })
}
})
document.body?.addEventListener('mouseout', () => {
window.api.setIgnoreMouseEvents(false)
})
}
hook 中的 setIgnoreMouseEvents 是主进程的穿透事件
ipcMain.on(
'setIgnoreMouseEvents',
(event: IpcMainEvent, ignore: boolean, options?: { forward: boolean }) => {
getWindowByEvent(event).setIgnoreMouseEvents(ignore, options)
}
)
然后在组件中使用该 hooks
const { setIgnoreMouseEvents } = useIgnoreMouseEvents()
setIgnoreMouseEvents()