操作系統(tǒng)原生的窗口樣式中規(guī)中矩逸尖,看久了卻難免會(huì)有些厭煩崔泵。所以在使用 electron
創(chuàng)建桌面應(yīng)用的時(shí)候颠通,有時(shí)候我們希望能完全掌控窗口的樣式,而隱藏掉系統(tǒng)提供的窗口邊框和標(biāo)題欄等。
通過(guò)在創(chuàng)建窗口的時(shí)候仅仆,指定{frame:false}
或{titleBarStyle: 'hidden'}
(macOS only)即可達(dá)到隱藏邊框的效果器赞,甚至可以通過(guò) {transparent:true}
來(lái)指定窗口透明,創(chuàng)建異形的窗口呈現(xiàn)蝇恶。具體可見(jiàn)官方文檔拳魁,這里不再贅述。
一個(gè)值得一提的問(wèn)題是窗口的拖動(dòng)撮弧。因?yàn)闆](méi)有標(biāo)題欄,所以需要自行實(shí)現(xiàn)窗口的拖動(dòng)區(qū)域姚糊,否則就沒(méi)法移動(dòng)窗口位置了贿衍。可能的實(shí)現(xiàn)方案有下面幾種:
方案一: -webkit-app-region: drag;
官方文檔里有詳細(xì)說(shuō)明:
默認(rèn)情況下, 無(wú)框窗口是 non-draggable 的救恨。 應(yīng)用程序需要指定 `-webkit-app-region: drag` 在 CSS 中告訴Electron哪個(gè)區(qū)域是可拖拽的 (像 OS 的標(biāo)準(zhǔn)標(biāo)題欄), 并且應(yīng)用程序也可以使用 `-webkit-app-region: no-drag` 來(lái)排除 draggable region 中的 non-draggable 區(qū)域贸辈。 請(qǐng)注意, 當(dāng)前只支持矩形形狀。
要使整個(gè)窗口可拖拽, 您可以添加 `-webkit-app-region: drag` 作為 `body` 的樣式:
<body style="-webkit-app-region: drag"></body>
請(qǐng)注意, 如果您已使整個(gè)窗口draggable, 則必須將按鈕標(biāo)記為 non-draggable, 否則用戶(hù)將無(wú)法單擊它們:
button { -webkit-app-region: no-drag; }
如果你設(shè)置自定義標(biāo)題欄為 draggable, 你也需要標(biāo)題欄中所有的按鈕都設(shè)為 non-draggable肠槽。
試下來(lái)拖拽效果很完美擎淤。但是,文檔后面提到了這種方法較為致命的一個(gè)問(wèn)題:
在某些平臺(tái)上, 可拖拽區(qū)域?qū)⒈灰暈?non-client frame, 因此當(dāng)您右鍵單擊它時(shí), 系統(tǒng)菜單將彈出秸仙。 要使上下文菜單在所有平臺(tái)上都正確運(yùn)行, 您永遠(yuǎn)也不要在可拖拽區(qū)域上使用自定義上下文菜單嘴拢。
不僅右鍵菜單,設(shè)置了這個(gè)樣式的元素幾乎無(wú)法響應(yīng)所有的鼠標(biāo)事件寂纪,包括點(diǎn)擊席吴、拖拽等。如果需要拖拽整個(gè)窗口捞蛋,就相當(dāng)尷尬了孝冒。
方案二:通過(guò)響應(yīng)頁(yè)面的 mousemove
事件
既然我們需要頁(yè)面能夠響應(yīng)鼠標(biāo)事件,那能不能就通過(guò)鼠標(biāo)事件去解決問(wèn)題呢拟杉?這是作為一名前端開(kāi)發(fā)人員很容易想到的方案:通過(guò)網(wǎng)頁(yè)的 mousemove
事件庄涡,我們可以得知當(dāng)前鼠標(biāo)在網(wǎng)頁(yè)上的坐標(biāo),并與上一次的坐標(biāo)進(jìn)行比較搬设,得出鼠標(biāo)的位移數(shù)值穴店。
然后,我們可以通過(guò)當(dāng)前 electron 窗口上的 getPosition
方法獲取窗口當(dāng)前位置焕梅,加上鼠標(biāo)位移得出新的位置迹鹅,然后通過(guò) setPosition
方法手動(dòng)移動(dòng)窗口,達(dá)到拖動(dòng)的效果贞言。
是不是看上去很美斜棚?很可惜不能高興得太早。網(wǎng)頁(yè)上的兩次 mousemove
事件之間有一定時(shí)間間隔,這個(gè)間隔對(duì)于桌面客戶(hù)端編程來(lái)說(shuō)有點(diǎn)太長(zhǎng)了弟蚀。這就導(dǎo)致在性能比較差的情況下蚤霞,有可能出現(xiàn)這樣的情況:鼠標(biāo)移動(dòng)過(guò)快,移出了窗口的范圍义钉,而下一次 mousemove
還沒(méi)來(lái)得及觸發(fā)昧绣。這樣窗口就跟不上鼠標(biāo),“掉下來(lái)”了……
方案三:electron-drag
在一度絕望的時(shí)候捶闸,發(fā)現(xiàn)了 electron-drag 這個(gè)庫(kù)和它天才的想法:通過(guò)一個(gè)原生 Node.js 模塊夜畴,跟蹤鼠標(biāo)在整個(gè)屏幕上的位移,然后手動(dòng)設(shè)置窗口的位置删壮。
這個(gè)庫(kù)只在 Windows 和 macOS 下可用贪绘,不支持 Linux。因此央碟,在不支持的平臺(tái)上税灌,需要使用方案一或者方案二進(jìn)行容。
一個(gè)小插曲
在 Windows 上引用 electron-drag
時(shí)可能會(huì)拋出 Uncaught Error: A dynamic link library (DLL) initialization routine failed
的錯(cuò)誤(macOS 上暫未遇到亿虽,不確定是否也有)菱涤。這是因?yàn)樵搸?kù)使用 win-mouse
和 osx-mouse
這兩個(gè)原生模塊進(jìn)行鼠標(biāo)位置的追蹤,而 electron 和系統(tǒng)中安裝的 Node.js 程序頭文件未必相同洛勉,要使用原生模塊必須使用正確版本的頭文件進(jìn)行編譯粘秆。解決方式是安裝 electron-rebuild
重新編譯對(duì)應(yīng)的模塊。Windows 下的操作如下:
electron-rebuild -f -w win-mouse
最后上代碼(TypeScript):
export function makeDraggable(el: HTMLElement | string) {
if (typeof el === 'string') { el = document.querySelector(el) as HTMLElement; }
try {
const drag = require('electron-drag');
if (drag.supported) {
drag(el);
} else {
makeDraggableFallback(el);
}
} catch (ex) {
makeDraggableFallback(el);
}
}
function makeDraggableFallback(el: HTMLElement) {
// 方案一
// el.style['-webkit-app-region'] = 'drag';
// 方案二
let dragging = false;
let mouseX = 0;
let mouseY = 0;
el.addEventListener('mousedown', (e) => {
dragging = true;
const { pageX, pageY } = e;
mouseX = pageX;
mouseY = pageY;
});
window.addEventListener('mouseup', () => {
dragging = false;
});
window.addEventListener('mousemove', (e: MouseEvent) => {
if (dragging) {
const { pageX, pageY } = e;
const win = require('electron').remote.getCurrentWindow();
const pos = win.getPosition();
pos[0] = pos[0] + pageX - mouseX;
pos[1] = pos[1] + pageY - mouseY;
win.setPosition(pos[0], pos[1], true);
}
});
}