之前有段時間在找各種可以做桌面小工具的方法,後來有發現 Rainmeter 這個工具,雖然他有自己完整的生態,但他的專案結構對我非常不友善,同時我也不熟 Lua 的開發所以其實我沒有使用這個工具做出自己喜歡的桌面小工具。
直到最近接觸到了 Electron 發現了新大陸。
要素
由於 Rainmeter 還算是有地位的桌面小工具,而他的呈現也與我對於桌面小工具應有的樣子一樣,所以這次我們借鑑 Rainmeter 來開發桌面小工具。
可以看到他具備以下要素
- 工作列不顯示小工具的視窗
- 小工具為透明視窗
- 小工具的視窗沒有邊框
- 位置-總是在最上層
- 桌面選釘
- 點擊穿透
接著我們就來根據這些元素來嘗是用 Electron 呈現。
實作
視窗設定
Electron 可以在創建視窗的時候設定視窗的資訊 我們可以用設定的方式完成第一點到第五點
const win = new BrowserWindow({
// 不顯示在任務欄
skipTaskbar:true,
// 透明背景
transparent: true,
// 沒有邊框
frame: false,
// 至頂視窗
alwaysOnTop: true,
// 視窗不能被最小化
minimizable: false,
// 視窗不能被最大化
maximizable: false,
// 視窗不能全螢幕
fullscreenable: false,
// 視窗不能被關閉
webPreferences: {
nodeIntegration: true,
contextIsolation: false // 否则页面无法用require
},
});
事件設定
我希望他可以點擊穿透,也就是讓視窗不要對滑鼠的事件做反應。這時候要用到 win.setIgnoreMouseEvents(true, { forward: true });
方法,可是我有時候要對小工具的元件進行操作阿。那就需要頻繁的開關。
我們可以藉由 Electron 自帶的行程間通訊 ipcMain
方法來進行通訊。如果收到 啟用點擊穿透 事件
,則在主進程中忽略所有滑鼠事件。反之,如果收到 關閉點擊穿透 則重新響應滑鼠事件。
main.js
// 忽略滑鼠事件
win.setIgnoreMouseEvents(true, { forward: true });
// 處理點擊穿透
ipcMain.on('win-penetrate-true', (event, arg) => {
BrowserWindow.fromWebContents(event.sender).setIgnoreMouseEvents(true, { forward: true });
});
ipcMain.on('win-penetrate-false', (event, arg) => {
BrowserWindow.fromWebContents(event.sender).setIgnoreMouseEvents(false);
});
index.js
const targetElements = document.getElementsByClassName("action-element")
for (let i = 0; i < targetElements.length; i++) {
const targetElement = targetElements[i];
targetElement.addEventListener('mouseenter', () => {
ipcRenderer.send('win-penetrate-false');
});
targetElement.addEventListener('mouseleave', () => {
ipcRenderer.send('win-penetrate-true');
});
}
我定義了一個 class 叫做 action-element
,這個 class 的元件會接收滑鼠事件,所以當滑鼠進入這個元素時使用IPC關閉點擊穿透,離開則開啟。
結尾
一照上面的設定這樣就可以得到一個乾淨的小工具了:D
至於移動的部分我還在研究,目前還沒找到一個我滿意的方案,等研究玩再發一篇文XD。
同場加映-自動設定小工具視窗大小
main.js
ipcMain.on('reset-window-size', (event, arg) => {
win.setSize(arg.width, arg.height);
});
index.js
// 重置視窗大小
function resetWindowSize(body) {
width = parseInt((body.getBoundingClientRect()).width) + parseInt(body.style.margin) * 2;
height = parseInt((body.getBoundingClientRect()).height) + parseInt(body.style.margin) * 2;
console.log(width, height);
ipcRenderer.send('reset-window-size', { width, height });
}