引入
如果你尝试过透明窗口,并控制透明部分事件击穿,就会发现使用 drag属性样式去控制窗口拖拽会导致点击事件失效,并且带drag属性的窗口移动到另一个窗口的透明部分会有窗口乱动的各种BUG,于是,这便需要我们自己去实现窗口拖拽移动。
demo项目地址
实现思路
参考这篇文章的实现思路
同网上给出的大多数实现,利用定时器+主进程的screen.getCursorScreenPoint(),在渲染进程开始移动时,让窗口黏在鼠标上,跟随鼠标移动,当停止拖拽时清除主进程的移动定时器。
网上找到的代码有两个常见BUG:
1.windows下,系统设置=>屏幕=>缩放如果设置的不是100% 【测试发现公司60%的非开发人员的缩放都是150%~125】,会导致拖拽窗口一直放大,如下:
2.windows【alt+tab】或mac,在进行快捷窗口切换时,如果此时鼠标是按压状态,结束切换会导致窗口一直黏在鼠标上,如下:
实现步骤
已解决以上所有BUG
1.主进程监听窗口移动
- electron\main\index.ts
- 利用定时器实时让当前窗口黏在鼠标上
- 通过重设窗口宽高,解决windows缩放不是100%时的缩放bug
- 通过修改位置前判断窗口销毁,来解决窗口被删除,但定时任务未结束,导致报错 调用已销毁窗口的错误
- 通过判断窗口是否失焦,来解决windows / mac 快捷切换窗口,导致窗口黏在鼠标上的BUG
/** 窗口移动功能封装 */
// 窗口移动 位置刷新定时器
let movingInterval = null;/*** 窗口移动事件*/
ipcMain.on("window-move-open", (event, canMoving) => {let winStartPosition = { x: 0, y: 0 };let mouseStartPosition = { x: 0, y: 0 };const currentWindow = getWindowByEvent(event);const currentWindowSize = currentWindow.getSize();if (currentWindow) {if (canMoving) {// 读取原位置const winPosition = currentWindow.getPosition();winStartPosition = { x: winPosition[0], y: winPosition[1] };mouseStartPosition = screen.getCursorScreenPoint();// 清除旧的定时器if (movingInterval) {clearInterval(movingInterval);}// 创建定时器,每10毫秒更新一次窗口位置,保证一致movingInterval = setInterval(() => {// 窗口销毁判断,高频率的更新有可能窗口已销毁,定时器还没结束,此时就会出现执行销毁窗口方法的错误if (!currentWindow.isDestroyed()) {// 如果窗口失去焦点,则停止移动if (!currentWindow.isFocused()) {clearInterval(movingInterval);movingInterval = null; }// 实时更新位置const cursorPosition = screen.getCursorScreenPoint();const x =winStartPosition.x + cursorPosition.x - mouseStartPosition.x;const y =winStartPosition.y + cursorPosition.y - mouseStartPosition.y;// 更新位置的同时设置窗口原大小, windows上设置=>显示设置=>文本缩放 不是100%时,窗口会拖拽放大currentWindow.setBounds({x: x,y: y,width: currentWindowSize[0],height: currentWindowSize[1],});}}, 10);} else {clearInterval(movingInterval);movingInterval = null;}}
});
2.通信工具补充ipc调用
- src\utils\electronUtils.ts
/**
* 窗口是否可以跟随鼠标移动
* @param flag
*/
export function windowMove(flag: boolean) {ipcRenderer.send("window-move-open", flag);
}
3.渲染进程封装通用拖拽组件
- src\components\DragTool.vue
<template><div@mouseenter="mouseenter"@mouseleave="mouseleave"@mousedown="mousedown"@mouseup="mouseup"><slot></slot></div>
</template><script setup lang="ts">
import electronUtils from "@/utils/electronUtils";
// 鼠标进入判断,只有鼠标进入到范围内,才能进行鼠标按压拖拽
let enterFlag = false;
// 鼠标按压判断,只有鼠标进入范围内,并且按压状态,此时释放鼠标才会关闭窗口移动
let mousedownFlag = false;/**鼠标按压 */
function mousedown() {if (enterFlag) {electronUtils.windowMove(true);mousedownFlag = true;}
}/**鼠标释放 */
function mouseup() {if (enterFlag && mousedownFlag) {electronUtils.windowMove(false);mousedownFlag = false;}
}/**鼠标移入 */
function mouseenter() {enterFlag = true;
}/**鼠标移出 */
function mouseleave() {enterFlag = false;
}
</script><style scoped lang="scss"></style>
测试
直接塞个拖拽盒子
- src\components\demo\Index.vue
<template><drag-tool><div class="drag-box">拖拽区域</div></drag-tool>
</template>
<style scoped lang="scss">
.drag-box {width: 200px;height: 50px;border: 1px solid #ccc;background: pink;margin: 0 auto;user-select: none;
}
</style>
最终效果如下:
- 修复前文展示的两个BUG