Electron 自定义托盘实战——桌面计算器
Electron 默认的托盘(Tray)提供了菜单能力,但是并不能让用户自定义窗口视图,有很大的局限性。我们经常看到 Mac 上很多原生应用,点击托盘之后可以弹出下拉窗口,那 Electron 能不能实现这个呢?其实是可以的,今天就带领大家一起实现一个「桌面计算器」。
首先,我们需要写一个无边框的 BrowserWindow 来加载计算器界面:
let win, tray
function createTrayWindow() {
const width = 300
const height = 420
win = new BrowserWindow({
width,
height,
frame: false,
resizable: false,
show: false,
movable: false,
minimizable: false,
maximizable: false,
})
win.loadFile(path.join(__dirname, '../renderer/calculator.html'))
}
注意这里 BrowserWindow 默认设置为不显示(show: false),只有当用户点击托盘图标的时候,才展示这个窗口。
这里的难点在于:托盘的位置是不固定的,要让窗口刚好显示在托盘图标的下方,那么每次点击托盘的时候,要实时计算一下托盘的位置。好在 Tray 有个 API 可以获取到图标的位置:
tray.getBounds() // 返回 { x, y, width, height }
该方法会返回一个矩形(Rectangle)对象,包含以下信息:
x
:托盘的 x 坐标y
:托盘的 y 坐标width
:托盘的宽度height
:托盘的高度
接下来,我们创建一个托盘,并为其绑定 click 事件:
const tray = new Tray(path.join(__dirname, 'calculatorTemplate.png'))
tray.on('click', () => {
const trayBounds = tray.getBounds()
win.setPosition(trayBounds.x + trayBounds.width / 2 - width / 2, trayBounds.height)
if (win.isVisible()) {
win.hide()
} else {
win.show()
}
})
创建 Tray 实例的时候,需要提供一个 image 参数,可以是本地图片的绝对路径,也可以是 NativeImage 的实例。关于 NativeImage 类后面会详细讲,本节先用普通图片文件来创建托盘。在 macOS 下,设置托盘的图片名称必须以 Template 结尾,一般需要提供 16x16(72dpi) 和 32x32@2x(144dpi) 的两个图片,例如:
- trayiconTemplate.png
- trayiconTemplate@2x.png
需要特别注意的是:模板图像必须由黑色和透明通道组成。使用模板图像作为菜单栏图标的好处是,它可以适应浅色和深色菜单栏。在 Windows 系统上则不需要按照这种命名规则了,建议在 Windows 上使用 ico 格式的图片,视觉效果会更好一点。
上面最关键的代码在于这一行,用于控制托盘出现的位置:
win.setPosition(trayBounds.x + trayBounds.width / 2 - width / 2 , trayBounds.height)
由于 BrowserWindow 的宽高已经固定下来了,如果要让窗口正好位于托盘图标的中间正下方,就要算好偏移量,窗口的坐标计算逻辑为:
- x 坐标:托盘中心位置的 x 坐标减去窗口的宽度
- y 坐标:托盘底部的 y 坐标(即托盘高度)
这样基础的效果就出来了:
不过此时窗口的显示和隐藏完全由托盘的点击事件控制,当用户切换到其他应用时,表现效果和普通的 BrowserWindow 一样,层级在当前聚焦的应用下方:
但托盘窗口更好的交互是:当窗口失去焦点的时候,自动隐藏。这也很简单,只要为窗口增加 blur 事件即可:
win.on('blur', () => {
win.hide()
})
这样程序就丝滑多了:
不过还有一个不容易发觉的不完美的地方:Mac 是支持多桌面的,用户可以通过四指左右滑动的方式左右切换桌面:
你会发现上面的计算器窗口在切换桌面之后不见了,计算器窗口只会出现在首次展示的桌面上,在其他桌面再点托盘图标,会自动切回第一个桌面,这种体验是很不好的:
不过,要修复这个问题也非常简单啦,只需要一行代码即可:
win.setVisibleOnAllWorkspaces(true)
对,就是字面意思:在所有工作空间上都展示。最后在打包的时候还有一点需要注意:一般的纯托盘应用,是不带 dock 图标的,打包的时候在 info.plist 里面添加 LSUIElement 配置项可解决这个问题:
<plist version="1.0">
<dict>
<key>LSUIElement</key>
<integer>1</integer>
</dict>
</plist>
具体的添加方法依赖于应用打包方式,以 electron-packager 为例,配置项为:
"mac": {
"extendInfo": {
"LSBackgroundOnly": 1,
"LSUIElement": 1
}
}
留给读者的思考题
上面的代码只适合在 macOS 系统运行,请将其移植到 Windows 系统上。
注意:在 Mac 和 Windows 上,托盘的位置是不一样的,Windows 默认是左下角,但是 Windows 系统上的任务栏的位置是可以让用户自定义的:
位置可以选择上下左右四个:
你要考虑到这四种场景下托盘窗口的显示位置才行,聪明的你是否有思路了呢?不妨在评论区交流!
转载自:https://juejin.cn/post/7202153876439089212