所有三个操作系统都提供了应用程序向用户发送通知的手段。 Electron允许开发者使用 HTML5 Notification API 发送通知,并使用当前运行的操作系统的本地通知 API 来显示它。
注意: 由于这是一个 HTML5 API,它只能在渲染器进程中使用。 如果你想在主进程中显示通知,请查看 Notification 模块.
let myNotification = new Notification('标题', {
body: '通知正文内容'
})
myNotification.onclick = () => {
console.log('通知被点击')
}
此方案报错: Notification未定义。待解决
补充方案: node-notifier
安装
npm install --save node-notifier
示例:
const notifier = require('node-notifier');
// String
notifier.notify('Message');
// Object
notifier.notify({
title: 'My notification',
message: 'Hello, there!'
});
效果如下
更多使用说明参考:
https://github.com/mikaelbr/node-notifier
虽然操作系统的代码和用户体验相似,但依然存在微妙的差异。
Electron尝试将应用程序用户模型 ID 的相关工作自动化。 Electron在和安装和更新框架 Squirrel 协同使用的时候,快捷方式将被自动正确的配置好。 更棒的是,Electron 会自动检测 Squirrel 的存在,并且使用正确的值来自动调用app.setAppUserModelId()。 During development, you may have to call app.setAppUserModelId() yourself.
此外,在Windows 8中,通知正文的最大长度为250个字符,Windows团队建议将通知保留为200个字符。 然而,Windows 10中已经删除了这个限制,但是Windows团队要求开发人员合理使用。 尝试将大量文本发送到API(数千个字符) 可能会导致不稳定。
Windows 的更高版本允许高级通知,自定义模板,图像和其他灵活元素。 要发送这些通知(来自主进程或渲染器进程),请使用用户区模块 electron-windows-notifications 来用原生节点附件发送 ToastNotification 和 TileNotification 对象。
虽然包含按钮的通知可以使用 electron-windows-notifications,但处理回复则需要使用electron-windows-interactive-notifications,这有助于注册所需的COM组件,并使用输入的用户数据调用Electron应用程序。
要检测是否允许发送通知,请使用用户区模块 electron-notification-state。
这样,您可以提前确定 Windows 是否会将通知忽略。
MacOS上的通知是最直接的,但你应该注意苹果关于通知的人机接口指南(Apple’s Human Interface guidelines regarding notifications).
请注意,通知的大小限制为256个字节,如果超过该限制,则会被截断。
后来的 macOS 版本允许有一个输入字段的通知,允许用户快速回复通知。 为了通过输入字段发送通知,请使用用户区模块node-mac-notifier。
要检测是否允许发送通知,请使用用户区模块 electron-notification-state。
这样可以提前检测是否显示通知。
通知是通过libnotify发送的,libnotify可以在任何实现了桌面通知规范(Desktop Notifications Specification)的桌面环境中发送通知,包括Cinnamon、Enlightenment、Unity、GNOME、KDE
在 Windows 中的任务栏按钮可以被用于显示一个进度条。 这可以让一个窗口提供进度信息给用户,而不必切自行切换到这个窗口。
在 macOS,进度条将显示为 dock 图标的一部分。
Unity DE 也具有同样的特性,在运行器上显示进度条。
任务栏按钮中的进度栏:
三个系统中都是用相同的API - setProgressBar() 方法是 BrowserWindows 的方法。 是用 0 到 1 之间的数字来表示你的进度。 你正在运行一个长时间的任务, 当前进度为63%, 您可以使用 setProgressBar(0.63) 来调用它。
一般来说,将参数设置为 0 以下的值(例如 -1)将会去掉进度条,而设置为 1 以上(例如 2)将会切换这个进度条为不确定的进度。
参见 API documentation for more options and modes。
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
win.setProgressBar(0.5)
Electron有API来配置Windows任务栏中的应用程序图标。 支持的有 创建一个 弹出列表, 自定义缩略图和工具栏, 图标叠加, 和所谓的 “闪烁框” 效果, 而且 Electron 还使用应用程序的 dock 图标来实现跨平台功能 比如 最近文档 和 应用进程.
Windows 允许应用程序自定义一个上下文菜单,用户右键单击任务栏中的应用图标可以看到该菜单。 该上下文菜单被成为 弹出列表. 您可以在弹出列表的 “任务” 类别中指定自定义操作, 来自 MSDN 的引用:
应用程序的“任务”列表的制定是基于程序的功能,用户能用它做一些快捷操作。 任务应当是与上下文无关的,因为它不需要程序运行就可以工作。 而且据统计,它们应该是用户在这个应用上使用最多的操作,例如: 撰写一封邮件或者在邮件程序里打开日历,word处理程序新建一个文档,以某一种模式启动应用程序,或者是启动应用程序的某些子命令。 一个应用程序不应当定义一些用户不需要的高级功能的或者只会使用一次的操作的菜单,以防止将菜单弄得杂乱无章,例如注册。 不要将“任务”功能用于广告操作,例如升级应用或者推广特价产品等等。
强烈推荐“任务”列表内容是静态的。 不管什么情形,也不管应用程序是什么状态,它都应该是保持不变的。 尽管这个列表是动态可变的,你应该注意,那些没想过这个列表会变的用户会被这个行为搞糊涂。
Internet Explorer 的 任务:
不同于 macOS 的dock菜单,Windows 上的用户任务表现得更像一个快捷方式,比如当用户点击一个任务,一个程序将会被传入特定的参数并且运行。
你可以使用 app.setUserTasks API 来设置你的应用中的用户任务:
const { app } = require('electron')
app.setUserTasks([
{
program: process.execPath,
arguments: '--new-window',
iconPath: process.execPath,
iconIndex: 0,
title: 'New Window',
description: 'Create a new window'
}
])
调用 app.setUserTasks 并传入空数组就可以清除你的任务列表:
const { app } = require('electron')
app.setUserTasks([])
当你的应用关闭时,用户任务仍然会被显示,因此在你的应用被卸载之前,任务的图标和程序的路径必须是存在的。
在 Windows,你可以在任务栏上添加一个按钮来当作应用的缩略图工具栏。 它为用户提供了一种访问特定窗口命令的方式, 而无需还原或激活该窗口。
在 MSDN,它的说明如下:
此工具栏只是常见的标准工具栏控件。 它最多拥有七个按钮。 每个按钮的 ID、图像、工具提示和状态都定义在结构中, 然后传递给任务栏。 应用程序可以根据其当前状态的要求, 显示、启用、禁用或隐藏缩略图工具栏中的按钮。
例如, Windows 媒体播放机可能提供标准的媒体传输控制, 如播放、暂停、静音和停止。
Windows Media Player 的缩略图工具栏:
你可以使用 BrowserWindow.setThumbarButtons 来设置你的应用的缩略图工具栏。
const { BrowserWindow } = require('electron')
const path = require('path')
const win = new BrowserWindow()
win.setThumbarButtons([
{
tooltip: 'button1',
icon: path.join(__dirname, 'button1.png'),
click () { console.log('button1 clicked') }
}, {
tooltip: 'button2',
icon: path.join(__dirname, 'button2.png'),
flags: ['enabled', 'dismissonclick'],
click () { console.log('button2 clicked.') }
}
])
调用 BrowserWindow.setThumbarButtons 并传入空数组即可清空缩略图工具栏:
const { BrowserWindow } = require(‘electron’)
const win = new BrowserWindow()
win.setThumbarButtons([])
在 Windows,任务栏按钮可以使用小型叠加层显示应用程序 状态,引用 MSDN 的文档:
图标叠加作为状态的上下文通知, 旨在否定需要一个单独的通知区域状态图标来将该信息传达给用户。 例如, 当前在通知区域中显示的 Microsoft Outlook 中的新邮件状态现在可以通过任务栏按钮上的叠加来表示。 同样, 您必须在开发周期中决定哪个方法最适合您的应用程序。 叠加图标用于提供重要的、长期的状态或通知, 如网络状态、messenger 状态或新邮件。 不应向用户显示不断变化的叠加或动画。
任务栏按钮的叠加:
要设置窗口的叠加层图标,可以使用 BrowserWindow.setOverlayIcon API:
const { BrowserWindow } = require(‘electron’)
let win = new BrowserWindow()
win.setOverlayIcon(‘path/to/overlay.png’, ‘Description for overlay’)
在Windows上,你可以突出显示任务栏按钮以获得用户的关注。 这与在macOS上弹跳停靠栏图标相似。 来自 MSDN 参考文档:
通常, 会闪现一个窗口, 通知用户该窗口需要注意, 但是该窗口当前没有键盘焦点。
要在 BrowserWindow 的任务栏按钮突出显示,可以使用 BrowserWindow.flashFrame API:
const { BrowserWindow } = require('electron')
let win = new BrowserWindow()
win.once('focus', () => win.flashFrame(false))
win.flashFrame(true)
不要忘记调用 flashFrame 方法参数为 false 来关闭突出显示。 在上面的示例中, 当窗口进入焦点时会调用它, 但您可能会使用超时或其他一些事件来禁用它。
配置本地和全局键盘快捷键
您可以使用 [Menu] 模块来配置快捷键,只有在 app 处于焦点状态时才可以触发快捷键。 为此,在创建 MenuItem时必须指定一个 [accelerator] 属性。
const { Menu, MenuItem } = require(‘electron’)
const menu = new Menu()
menu.append(new MenuItem({
label: ‘Print’,
accelerator: ‘CmdOrCtrl+P’,
click: () => { console.log(‘time to print stuff’) }
}))
你还可以在操作系统中配置不同的组合键。
{
accelerator: process.platform === ‘darwin’ ? ‘Alt+Cmd+I’ : ‘Ctrl+Shift+I’
}
当应用程序不处于焦点状态时,你可以使用 globalShortcut 模块来检测键盘事件,
const { app, globalShortcut } = require(‘electron’)
app.whenReady().then(() => {
globalShortcut.register(‘CommandOrControl+X’, () => {
console.log(‘CommandOrControl+X is pressed’)
})
})
如果你想处理 BrowserWindow中的键盘快捷键,你可以监听渲染进程中 window 对象的 keyup 和 keydown 事件。
window.addEventListener(‘keyup’, doSomething, true)
注意第三个参数 true,这意味着当前监听器总是在其他监听器之前接收按键,以避免其它监听器调用 stopPropagation()。
在调度页面中的keydown和keyup事件之前,会发出before-input-event事件。 它可以用于捕获和处理在菜单中不可见的自定义快捷方式。
如果您不想手动进行快捷键解析,可以使用一些库来进行高级的按键检测。例如 mousetrap.
Mousetrap.bind('4', () => { console.log('4') })
Mousetrap.bind('?', () => { console.log('show shortcuts!') })
Mousetrap.bind('esc', () => { console.log('escape') }, 'keyup')
// 组合
Mousetrap.bind('command+shift+k', () => { console.log('command shift k') })
// 将多个组合映射到相同的回调
Mousetrap.bind(['command+k', 'ctrl+k'], () => {
console.log('command k or control k')
// 返回 false 以防止默认行为,并阻止事件冒泡
return false
})
// gmail 风格序列
Mousetrap.bind('g i', () => { console.log('go to inbox') })
Mousetrap.bind('* a', () => { console.log('select all') })
// konami 代码
Mousetrap.bind('up up down down left right left right b a enter', () => {
console.log('konami code')
})
在渲染进程中, Online and offline 事件检测,是通过标准 HTML5 API 中 navigator.onLine 属性来实现的。 脱机时 (从网络断开), navigator.onLine 属性将返回 false, 除此之外都返回true 。 由于所有其他条件都返回 true, 因此必须警惕信息误报, 因为我们不能保证 true 的情况下 Electron 一定可以访问 internet。 例如当软件运行在一个虚拟网络适配器始终为“connected”的虚拟机中时,Electron就不能访问Internet。 因此,如果你想确保 Electron 真实的网络访问状态,你应该开发额外的检测方法。
示例:
main.js
const { app, BrowserWindow } = require('electron')
let onlineStatusWindow
app.whenReady().then(() => {
onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false })
onlineStatusWindow.loadURL(`file://${__dirname}/online-status.html`)
})
online-status.html
<!DOCTYPE html>
<html>
<body>
<script>
const alertOnlineStatus = () => {
window.alert(navigator.onLine ? 'online' : 'offline')
}
window.addEventListener('online', alertOnlineStatus)
window.addEventListener('offline', alertOnlineStatus)
alertOnlineStatus()
</script>
</body>
</html>
效果:
测试结果不通过,待解决。
也会有人想要在主进程也有回应这些事件的实例。 然后主进程没有 navigator 对象因此不能直接探测在线还是离线。 使用 Electron 的进程间通讯工具,事件就可以在主进程被使用,就像下面的例子.
main.js
const { app, BrowserWindow, ipcMain } = require('electron')
let onlineStatusWindow
app.whenReady().then(() => {
onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false, webPreferences: { nodeIntegration: true } })
onlineStatusWindow.loadURL(`file://${__dirname}/online-status.html`)
})
ipcMain.on('online-status-changed', (event, status) => {
console.log(status)
})
online-status.html
<!DOCTYPE html>
<html>
<body>
<script>
const { ipcRenderer } = require('electron')
const updateOnlineStatus = () => {
ipcRenderer.send('online-status-changed', navigator.onLine ? 'online' : 'offline')
}
window.addEventListener('online', updateOnlineStatus)
window.addEventListener('offline', updateOnlineStatus)
updateOnlineStatus()
</script>
</body>
</html>
作为桌面程序,当然希望能够实现操作系统的 drag & drop 功能。 很多网站已经支持拖拽文件, Electron 当然也支持
要在 app 中实现此功能 ,你需要在 Render 进程中调用webContents.startDrag(item) API, 此API会给 Main 进程发送一个ondragstart事件。
在 Render 进程中, 接收 ondragstart 事件并发送消息到 Main 进程。
<a href="#" id="drag">item</a>
<script type="text/javascript" charset="utf-8">
document.getElementById('drag').ondragstart = (event) => {
event.preventDefault()
ipcRenderer.send('ondragstart', '/path/to/item')
}
</script>
然后, 在主进程中,接收拖拽过来的文件路径和在拖拽过程中要显示的图标。
const { ipcMain } = require('electron')
ipcMain.on('ondragstart', (event, filePath) => {
event.sender.startDrag({
file: filePath,
icon: '/path/to/icon.png'
})
})
效果: 没看到,待解决
离线渲染允许您在位图中获取浏览器窗口的内容,因此可以在任何地方渲染,例如在3D场景中的纹理。 Electron中的离屏渲染使用与 Chromium Embedded Framework 项目类似的方法。
可以使用两种渲染模式,并且只有脏区通过 ‘paint’ 事件才能更高效。 渲染可以停止、继续,并且可以设置帧速率。 指定的帧速率是上限值,当网页上没有发生任何事件时,不会生成任何帧。 最大帧速率是60,因为再高没有好处,而且损失性能。
注意: 屏幕窗口始终创建为 Frameless Window.
GPU加速
GPU加速渲染意味着使用GPU用于合成。 因为帧必须从需要更多性能的GPU中复制,因此这种模式比另一个模式慢得多。 The benefit of this mode is that WebGL and 3D CSS animations are supported.
软件输出设备
此模式使用软件输出设备在CPU中渲染,因此帧生成速度更快,因此此模式优先于GPU加速模式。
要启用此模式,必须通过调用 app.disableHardwareAcceleration() API 来禁用GPU加速。
const { app, BrowserWindow } = require('electron')
app.disableHardwareAcceleration()
let win
app.whenReady().then(() => {
win = new BrowserWindow({
webPreferences: {
offscreen: true
}
})
win.loadURL('http://github.com')
win.webContents.on('paint', (event, dirty, image) => {
// updateBitmap(dirty, image.getBitmap())
})
win.webContents.setFrameRate(30)
})