文章目录
- 引入
- 实现思路
- 实现步骤
- 1.主进程监听窗口移动
- 2.通信工具补充ipc调用
- 3.渲染进程封装通用拖拽组件
- 测试
引入
如果你尝试过透明窗口,并控制透明部分事件击穿,就会发现使用 drag属性样式去控制窗口拖拽会导致点击事件失效,并且带drag属性的窗口移动到另一个窗口的透明部分会有窗口乱动的各种BUG,于是,这便需要我们自己去实现窗口拖拽移动。
demo项目地址
实现思路
参考这篇文章的实现思路
同网上给出的大多数实现,利用定时器+主进程的screen.getCursorScreenPoint(),在渲染进程开始移动时,让窗口黏在鼠标上,跟随鼠标移动,当停止拖拽时清除主进程的移动定时器。
网上找到的代码有两个常见BUG:
1.windows下,系统设置=>屏幕=>缩放如果设置的不是100% 【测试发现公司60%的非开发人员的缩放都是125%~150%】,会导致拖拽窗口一直放大,如下:
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 = BrowserWindow.getFocusedWindow();
// 清除旧的定时器
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