實現一個不規則窗體

這裡我們實現一個圓形窗體,實現其他形狀的窗體與這個方法類似。

首先,把視窗的高度(height)和寬度(width)值修改為相同的值,使視窗成為一個正方形。

其次,把視窗的透明屬性(transparent)設定為true,這樣設定之後視窗還是正方形的,但只要我們控制好內容區域的Dom元素的形狀,就可以讓視窗看起來像一個不規則形狀一樣。

不規則視窗往往需要自定義邊框和標題欄,所以frame也設定為false。

另外,透明的視窗不可調整大小。所以將resizable屬性設定為false。

視窗顯示後,為了防止雙擊視窗可拖拽區觸發最大化事件,我們把maximizable屬性也設定為false。

最終建立視窗的程式碼如下:

win = new BrowserWindow({
width: 380,
height: 380,
transparent: true,
frame: false,
resizable: false,
maximizable: false,
//...
})

接下來再修改樣式,使內容區域的Dom元素呈現一個圓形:

html,body {
margin: 0px;
padding: 0px;
pointer-events: none;
}
#app {
box-sizing: border-box;
width: 380px;
height: 380px;
border-radius: 190px;
border: 1px solid green;
background: #fff;
overflow: hidden;
pointer-events: auto;
}

上面樣式程式碼中通過border-radius樣式把#app元素設定成了圓形。border-radius負責定義一個元素的圓角樣式,如果圓角足夠大,整個DIV就變成了一個圓形。

pointer-events樣式,在後面會有講解。

最終實現的視窗介面如圖5-7:

如果你略微瞭解CSS,你會知道除了圓形,你還可以通過CSS樣式控制這個視窗成為任意其他形狀。

點選穿透透明區域

上面這個應用會有一點小問題,雖然視窗看起來是圓形的,但它其實還是一個正方形視窗,只不過正方形四個角是透明的,所以看起來像一個圓形的視窗。

當我點選下圖中的①區域內的文字檔案時,滑鼠的點選事件還是發生在本視窗內,而不會點選到那個檔案上。

作為開發者,我們知曉其中的道理,但作為使用者來說,這就顯得很詭異。為了達到更好的使用者體驗,我們需要讓滑鼠在這4個區域發生點選動作時,點選動作可以穿透本視窗,落在視窗後面的內容上。

Electron官方文件明確說“不能點選穿透透明區域”,這並沒有難倒我們,有一個小trick來解決這個問題。

首先,需要用到視窗物件的setIgnoreMouseEvents方法,該方法可以使視窗忽略視窗內的所有滑鼠事件,並且在此視窗中發生的所有滑鼠事件都將被傳遞到此視窗背後的內容。

如果呼叫該方法時傳遞了forward引數,如:

setIgnoreMouseEvents(true, { forward: true }),

則只有點選事件會穿透視窗,滑鼠移動事件仍會觸發。

基於此,我們在頁面中執行如下程式碼:

  const remote = require("electron").remote;
let win = remote.getCurrentWindow();
window.addEventListener("mousemove", event => {
let flag = event.target === document.documentElement;
if (flag){
win.setIgnoreMouseEvents(true, { forward: true });
}
else {
win.setIgnoreMouseEvents(false);
}
});
win.setIgnoreMouseEvents(true, { forward: true });

注意,這是實驗程式碼,所以用了remote模組,關於remote模組的一些問題,我在“Electron團隊為什麼要幹掉remote模組“有詳細描述。

上面的程式碼中,設定視窗物件監聽mousemove事件,當滑鼠移入視窗圓形內容區的時候,不允許滑鼠事件穿透。當滑鼠移入透明區時,允許滑鼠事件穿透。

接著我們為html,body元素增加樣式:pointer-events: none,為#app元素增加樣式pointer-events: auto。

設定pointer-events: none後,其所標誌的元素就永遠不會成為滑鼠事件的target了。

為子元素#app設定了pointer-events: auto,說明子元素#app還是可以成為滑鼠事件的target的。

也就是說除了圓形區域內可以接收滑鼠事件外,其他部分將不再接收滑鼠事件。

當滑鼠在圓形區域外移動時,視窗物件的mousemove事件觸發,event.target為document.documentElement物件(這個事件並不是在html或body元素上觸發的,而是在視窗物件上觸發的,document.documentElement就是DOM樹中的根元素,也就是html節點所代表的元素)。

至此,上文程式碼中的判斷成立,當滑鼠在前文所述四個區域移動時,滑鼠事件允許穿透。滑鼠在圓形區域移動時,滑鼠事件不允許穿透。

至此,上文所述判斷成立,執行程式,滑鼠在正方形四角區域內點選,滑鼠事件具備了穿透效果。