electron+vue3全家桶+vite项目搭建【21】自定义无边框窗口拖拽移动

  • Post author:
  • Post category:vue




BUGFIX

2023/07/16 DragTool组件补充鼠标移除事件,添加延时器,避免卡顿的情况下,鼠标滑出移动范围,但窗口仍跟随鼠标移动



引入

如果你尝试过透明窗口,并控制透明部分事件击穿,就会发现使用 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调用

/**
* 窗口是否可以跟随鼠标移动
* @param flag
*/
export function windowMove(flag: boolean) {
	ipcRenderer.send("window-move-open", flag);
}



3.渲染进程封装通用拖拽组件

<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;
let timer: NodeJS.Timeout | null;

/**鼠标按压 */
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;
  // 避免卡顿的情况下,鼠标滑出移动范围,但窗口仍跟随鼠标移动
  if (timer !== null) {
    timer = setTimeout(() => {
      mousedownFlag = false;
      electronUtils.windowMove(false);
      timer = null;
    }, 1000);
  }
}
</script>

<style scoped lang="scss"></style>




测试

直接塞个拖拽盒子

<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

    在这里插入图片描述



版权声明:本文为qq_42365534原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。