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

news2024/12/23 1:18:31

文章目录

    • 引入
    • 实现思路
    • 实现步骤
      • 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
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/748790.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有没有后端程序员想要兼职的?

有一个非常有意思的现象&#xff1a; 后端看不起前端&#xff0c;认为前端是好入门&#xff0c;含金量低&#xff0c;一下就能学会的页面侠&#xff1b; 前端看不起后端&#xff0c;认为后端是每天CRUD、调参、拿来主义的搬砖工&#xff1b; 而实际上&#xff0c;大家都是给老板…

vue生命周期四个阶段(created和mount)

1.四个阶段 1&#xff09;必经阶段 2&#xff09;非必经阶段 提示&#xff1a;主动调用 vm.$destroy() 函数销毁后&#xff0c;可用 vm.$mount("#app") 将断开的 new Vue() 和页面重新建立虚拟 DOM 树&#xff0c;重新绑定起来挂载界面。 2. 生命周期钩子函数&…

【大数据】大数据简介

大数据简介 大数据基础平台架构实际应用关键技术 Hadoop 分布式计算平台Hadoop生态系统Hadoop安装和使用 HDFS分布式文件系统NamenodeSecondary NamenodeDataNodeblock 大数据基础 平台架构 实际应用 关键技术 Hadoop 分布式计算平台 Hadoop生态系统 Hadoop安装和使用 参考htt…

单向链表基本操作

目录 初始化链表 插入 删除 遍历 销毁 清空 初始化链表 代码&#xff1a; struct LinkNode* Init_LinkList() {struct LinkNode* head (struct LinkNode*)malloc(sizeof(struct LinkNode));head->data -1;head->next NULL;// 尾部指针struct LinkNode* pRear …

概率论的学习和整理15: 超几何分布,二项分布,泊松分布是如何趋近收敛的?

目录 1 问题&#xff1a; 2 结论 3 实验1 4 实验2 5 实验3 6 实验4 5 各种规律总结 5.1 1 5.2 2 5.3 3 5.4 4 6 超几何分布&#xff0c;二项分布&#xff0c;泊松分布&#xff0c;三者用EXCEL模拟 6.1 简单的扩展到泊松分布 6.2 比较整体的动态过程&…

在qt界面上内嵌拥有独立句柄的窗口

背景 在qt程序中&#xff0c;如果数据刷新频率过高&#xff0c;容易造成窗口卡顿&#xff0c;因为qt程序是整个窗口刷新&#xff0c;在此种背景下可以在qt程序的主程序上内嵌一个拥有独立句柄的窗口&#xff0c;两个窗口刷新就互不干扰。 案例 以下例子&#xff0c;在主窗口…

【技术指南】3D转换工具HOOPS Exchange的功能特征和典型使用场景全解析(一)

一、什么是 HOOPS Exchange&#xff1f; HOOPS Exchange 是一组软件库&#xff0c;可以帮助开发人员在开发应用程序时读取和写入主流的 2D 和 3D 格式。HOOPS Exchange 支持 在主流的3D 文件格式中读取 CAD 数据&#xff0c;并支持将 3D 数据转换为 PRC 数据格式&#xff0c;…

2.2 顺序表与链表特性对比

1. 插入删除操作对比 1. 顺序表插入删除元素 插入策略: 在某个位置插入元素时, 把从该位置开始的所有元素都往后挪一个位置, 规定顺序表最后一个元素后面的位置也是一个可插入位置. 后面的元素先往后挪动位置. 删除策略: 在某位置删除元素, 把从该位置之后的所有元素都往前…

【技术篇】• 饮用水除硝酸盐的技术解析

​​​​​​​ 近年来由于农业活动及排污物的影响&#xff0c;部分地表水源水中硝酸盐含量呈现明显的增加趋势&#xff0c;硝酸盐污染成为地下水和饮用水领域关注的热点问题之一。 硝酸盐是有氧环境中稳定的含氮化合物形式&#xff0c;也是含氮有机物通过无机化分解的产物&am…

搭建archetype骨架工程

搭建archetype骨架工程 一、archetype概念1、archetype简介2、archetype组成结构3、archetype生命周期4、archetype使用 二、构建我们自定义的骨架工程1、创建一个自定义的项目2、修改pom的build插件3、生成archetype资源文件4、将生成的资源文件制作成archetype jar包5、生成a…

Java中不同变量声明类型

今天在学习分层解耦-三层架构的过程中&#xff0c;具体文章参照&#xff1a;写文章-CSDN创作中心 在Servie层创建Dao对象时&#xff0c;以及在Controller层创建Service对象时&#xff0c;发现与我之前了解的声明变量的方法不一样。具体关键代码如下&#xff1a; 其中EmpServic…

简要介绍 | 边缘计算:原理,研究现状与未来展望

注1&#xff1a;本文系“简要介绍”系列之一&#xff0c;仅从概念上对边缘计算进行非常简要的介绍&#xff0c;不适合用于深入和详细的了解。 边缘计算&#xff1a;原理&#xff0c;研究现状与未来展望 What is Edge Computing? | Moving Intelligence to the Edge 一、背景介…

小平板 大智慧-嵌入式方案满足教育市场多元需求

线上教育观念的深入和技术的更新&#xff0c;直接拉动了教育类硬件及相关终端设备的市场需求。 产品框图 IDO-SBC3566采用瑞芯微RK3566&#xff0c; CPU采用4核A55架构处理器&#xff0c;集成G52图形处理器&#xff0c;内置独立NPU&#xff0c;算力高达1Tops&#xff0c;可满足…

如何在 Windows 中免费合并 PDF 文件 [在线和离线]

PDF是一种广泛使用的文件格式&#xff0c;具有兼容性好、安全性高、易于打印、方便浏览等众多优点。在工作和学习过程中&#xff0c;经常需要将同一类型的PDF文件合并起来&#xff0c;以方便传输和查看&#xff0c;使得合并PDF文件成为一种重要的数据整合方法。 如果您想知道如…

Es存储和查询

基本概念 Cluster 集群&#xff0c;一个ES集群是由多个节点(Node)组成的&#xff0c;每个集群都有一个cluster name 作为标识&#xff0c; 在同一网段下的Es实例会通过cluster name 决定加入哪个集群下。 node 节点&#xff0c;一个ES实例就是一个node&#xff0c;一个机器可以…

电气控制与PLC之间的本质区别是什么?

电气控制和PLC&#xff08;可编程逻辑控制器&#xff09;之间的最实质的区别是它们的实现方式和应用范围。 我这里刚好有嵌入式、单片机、plc的资料需要可以私我或在评论区扣个6 实现方式&#xff1a; 电气控制&#xff1a;电气控制是通过使用电气元件&#xff08;如继电器、…

数据库多表连接查询练习

数据库多表连接查询练习 一、创建数据库dept、emp 二、插入数据 1.找出销售部门中年纪最大的员工的姓名 mysql> select name,age from emp inner join dept on dept.dept1emp.dept2 where dept_name销售 order by age desc limit 1;2.求财务部门最低工资的员工姓名…

探索AI大模型:现状、挑战与未来

导言&#xff1a; 近年来&#xff0c;AI大模型如GPT&#xff08;生成对抗网络&#xff09;在自然语言处理领域崭露头角&#xff0c;引发广泛关注和讨论。这些模型能够生成各种类型的文本内容&#xff0c;展现出惊人的语言处理能力。然而&#xff0c;AI大模型的发展也面临着挑战…

Perl web - Mojolicious 小记

文章目录 关于 Mojolicious安装 关于 Mojolicious Mojolicious是Perl语言比较流行的异步Web开发框架。 官网&#xff1a;https://mojolicious.orggithub : https://github.com/mojolicious/mojo示例&#xff1a;https://github.com/mojolicious/mojo/tree/main/examples 相关…

【雕爷学编程】Arduino动手做(138)---64位WS2812点阵屏模块8

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…