我如何让 Electron 窗口可拖动并保持鼠标进入/离开检测
Source: Dev.to
介绍
我希望我的应用能够完全可拖动,同时在鼠标 enter 和 leave 时仍然能够改变其高度和宽度。默认情况下,使窗口可拖动会忽略拖拽区域的鼠标事件,这会导致普通的 enter/leave 检测失效。
可视化对比
鼠标在外部
鼠标在内部
窗口配置
const mainWindow = new BrowserWindow({
// width, height, x, y, etc.
titleBarStyle: 'hidden',
backgroundMaterial: "acrylic",
visualEffectState: "active",
vibrancy: "popover", // macOS
frame: false, // frameless window
});
让窗口可拖动
为任何需要充当拖拽区域的区域添加以下类:
.dragAble {
-webkit-user-select: none;
-webkit-app-region: drag;
}
对于必须保持交互性的元素,使用:
.no-drag {
-webkit-app-region: no-drag;
}
处理 mouse‑enter 和 mouse‑leave
Electron 并未为整个窗口提供原生的 mouse‑enter/leave 事件,所以我们自行实现。
1. 轮询监听器
一个工具函数在一定间隔内运行,检查光标位置是否在窗口边界内,并将结果通过回调返回。
function listenToMouseMovement(callback) {
const id = setInterval(() => {
const cursor = screen.getCursorScreenPoint();
const bounds = esm.mainWindow.getBounds();
const inside =
cursor.x >= bounds.x &&
cursor.x = bounds.y &&
cursor.y {
if (esm.isMovingWindow) return; // ignore while dragging
if (inside) {
// Cancel any pending leave timeout
if (leaveTimeout) {
clearTimeout(leaveTimeout);
leaveTimeout = null;
}
if (esm.mouseWasOutsideWindow) {
// Debounce the enter event
if (!enterTimeout) {
enterTimeout = setTimeout(() => {
esm.mouseWasOutsideWindow = false;
mainWindow.webContents.send("onMouseIsInsideTheApp");
}, 120);
}
}
} else {
// Cancel any pending enter timeout
if (enterTimeout) {
clearTimeout(enterTimeout);
enterTimeout = null;
}
if (!esm.mouseWasOutsideWindow) {
// Debounce the leave event
if (!leaveTimeout) {
leaveTimeout = setTimeout(() => {
esm.mouseWasOutsideWindow = true;
mainWindow.webContents.send("onMouseIsOutsideTheApp");
}, 120);
}
}
}
});
3. 窗口移动时忽略鼠标检查
我们使用 Electron 的原生事件来跟踪窗口的移动状态:
esm.mainWindow.on("will-move", () => {
esm.isMovingWindow = true;
});
esm.mainWindow.on("move", () => {
esm.isMovingWindow = true;
if (esm.moveEndTimeout) {
clearTimeout(esm.moveEndTimeout);
esm.moveEndTimeout = null;
}
});
esm.mainWindow.on("moved", () => {
if (esm.moveEndTimeout) clearTimeout(esm.moveEndTimeout);
esm.moveEndTimeout = setTimeout(() => {
esm.isMovingWindow = false;
esm.moveEndTimeout = null;
}, 200);
});
结果
该方案消除了闪烁,并且在窗口仍然能够完整拖动的同时,仍然响应 mouse‑enter 和 mouse‑leave 事件。你可以在这条推文中看到实际效果(为简洁起见省略链接)。
感谢阅读!这是我的第一篇文章,后续还有更多内容。

