Skip to content

常见问题

苹果安装失败

向军大叔每晚八点在 抖音bilibli 直播

xj-small

如果软件执行时出现 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()
})

任务栏图标

下面是主进程代码,用于定义任务栏图标

image-20230606035503290
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.houdunren.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;
}

鼠标事件

当给某个子元素设置了 -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)
  })
}

无边框圆角

下面介绍无边框圆角窗口的定义,但是由于不同系统的限制,苹果系统表现良好,有些系统默认不会有效果。

image-20230116000218529

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)
  }
})

编译后打开新窗口

有这个场景,我们想在打开新窗口时,显示 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'
    })
  )
}