electron+vue3全家桶+vite项目搭建【29】封装窗口工具类【3】控制窗口定向移动
文章目录
- 引入
- 实现效果
- 思路
- 声明通用的定位对象
- 主进程模块
- 渲染进程
- 测试效果
引入
demo项目地址
窗口工具类系列文章:
封装窗口工具类【1】雏形
封装窗口工具类【2】窗口组,维护窗口关系
封装窗口工具类【3】控制窗口定向移动
很多时候,我们想直接让某个窗口移动到边角,例如居中、左上、右下等位置,electron没有提供直接的api来实现这种操作,我们不妨基于electron的位置移动api进行封装,实现窗口灵活的定向移动
实现效果
- 输入key可以操作指定窗口,不输入则操作本窗口
- 可以直接根据position参数快速控制窗口定向移动
- 输入偏移量微调窗口
思路
electron位置相关api
electron获取某个窗口所在屏幕的显示对象
首先electron的窗口位置也是有(x,y)坐标,对应横轴和纵轴,我们可以使用窗口移动api设置x和y,左上角就是[0,0],然后我们可以通过screen.getDisplayMatching(rect)
来获取窗口所在屏幕的展示对象,从中可获取屏幕的长宽和x,y等信息,接着咱们直接用这两个数据做差即可:
假如屏幕用s表示,窗口用w表示,窗口居中计算如下:
x = s.x + (s.width - w.width) / 2
y = s.y + (s.height - w.height) / 2
声明通用的定位对象
- types\global.d.ts
/** 一些全局的对象补充声明 */
export {};
declare global {....../*** ================= 窗口定位相关参数 ================*//** 自定义定位类型 */type MyPosition =| "center" // 居中| "bottom-left" // 左下| "bottom-right" // 右下| "top-right" // 右上| "top-left"; // 左上/*** 注意,这里指的屏幕是当前窗口所在的屏幕【存在多个屏幕的情况下】*/interface IWindowPosition {windowKey?:string; // 窗口的key,不传就是修改本窗口的位置x?: number; // 屏幕左上角开始x轴位置y?: number; // 屏幕左上角开始y轴位置relativeWindowId?: number; // 相对于某个窗口的位置/默认相对于屏幕position?: MyPosition; // 相对于屏幕或窗口的位置offsetX?: number; // X轴偏移量offsetY?: number; // Y轴偏移量}
}
主进程模块
1.我们在窗口工具类中补充一个修改窗口位置的方法
- electron\main\windowUtils.ts
- 这里还补充了一个横纵轴的偏移量offsetX,offsetY,用于需要微调定位的情况
- 这里我们把window作为参数传入,而不是在方法内部直接通过key去获取,是为了方便后续其他在工具类中的位置修改操作方便调用
import {screen} from "electron";export class WindowUtils {...
/*** 修改窗口的位置* @param window 窗口对象* @param windowPosition 位置参数对象*/changeWindowPostion(window: BrowserWindow, windowPosition: IWindowPosition) {// xy轴值let x = windowPosition.x;let y = windowPosition.y;if (x != null && y != null) {// 偏移量const offsetX = windowPosition.offsetX || 0;const offsetY = windowPosition.offsetY || 0;x = windowPosition.x + offsetX;y = windowPosition.y + offsetY;// 如果是相对于某个窗口的话,加上相对窗口的x、y坐标if (windowPosition.relativeWindowId) {const relativeWin = BrowserWindow.fromId(windowPosition.relativeWindowId);if (relativeWin) {x += relativeWin.getPosition()[0];y += relativeWin.getPosition()[1];}}window.setPosition(x, y);}// 如果有定位if (windowPosition.position) {// 偏移量const offsetX = windowPosition.offsetX || 0;const offsetY = windowPosition.offsetY || 0;const winBounds = window.getBounds();let relativeBounds = screen.getDisplayMatching(winBounds).bounds;if (windowPosition.relativeWindowId) {const relativeWin = BrowserWindow.fromId(windowPosition.relativeWindowId);if (relativeWin) {relativeBounds = relativeWin.getBounds();}}// 计算坐标switch (windowPosition.position) {case "center":window.setPosition(relativeBounds.x +(relativeBounds.width - winBounds.width) / 2 +offsetX,relativeBounds.y +(relativeBounds.height - winBounds.height) / 2 +offsetY);break;case "bottom-left":window.setPosition(relativeBounds.x + offsetX,relativeBounds.y +relativeBounds.height -winBounds.height +offsetY);break;case "bottom-right":window.setPosition(relativeBounds.x + relativeBounds.width - winBounds.width + offsetX,relativeBounds.y +relativeBounds.height -winBounds.height +offsetY);break;case "top-left":window.setPosition(relativeBounds.x + offsetX,relativeBounds.y + offsetY);break;case "top-right":window.setPosition(relativeBounds.x + relativeBounds.width - winBounds.width + offsetX,relativeBounds.y + offsetY);break;}}}
}
2.接着我们补充两个获取窗口的方法
- electron\main\windowUtils.ts
export class WindowUtils {.../*** 通过窗口事件获取发送者的窗口* @param event ipc发送窗口事件*/getWindowByEvent(event: Electron.IpcMainInvokeEvent): BrowserWindow {const webContentsId = event.sender.id;for (const currentWin of BrowserWindow.getAllWindows()) {if (currentWin.webContents.id === webContentsId) {return currentWin;}}return null;}/*** 通过传入的key获取指定的窗口* @param key 窗口唯一key*/getWindowByKey(key: string): BrowserWindow {return BrowserWindow.fromId(this.group.get(key).windowId);}
}
3.接着我们在listen方法中补充handle监听,来处理渲染进程的事件
- electron\main\windowUtils.ts
export class WindowUtils {...listen() {...// 窗口位置修改监听ipcMain.handle(CustomChannel.window_position_change,(_, windowPosition: IWindowPosition) => {// 假如传了窗口的key,则获取对应窗口,假如没传,则用发送事件的窗口const windowKey = windowPosition.windowKey;const cureentWin =windowKey && windowKey.length > 0? this.getWindowByKey(windowKey): this.getWindowByEvent(_);this.changeWindowPostion(cureentWin, windowPosition);});}
}
渲染进程
1.工具类中补充窗口位置修改方法调用
- src\utils\electronUtils.ts
/*** 修改当前窗口的位置* @param windowPosition 窗口位置修改参数*/
export function changeWindowPosition(windowPosition: IWindowPosition) {ipcRenderer.invoke(CustomChannel.window_position_change, windowPosition);
}
2.写一个功能展示的demo
- src\components\demo\WindowPositionDemo.vue
<template><div class="mockDemo"><GoBack></GoBack><ul><li><el-input v-model="windowKey"></el-input></li><li><el-form inline><el-form-item label="距离左边距离"><el-input v-model="x"></el-input></el-form-item><el-form-item label="距离上边距离"><el-input v-model="y"></el-input></el-form-item><el-form-item label="距离左边偏移量"><el-input v-model="offsetX"></el-input></el-form-item><el-form-item label="距离上边偏移量"><el-input v-model="offsetY"></el-input></el-form-item><el-form-item><el-button@click="changeWindowPosition({x: Number.parseFloat(x),y: Number.parseFloat(y),offsetX: Number.parseFloat(offsetX),offsetY: Number.parseFloat(offsetY),windowKey,})">移动位置</el-button></el-form-item></el-form></li><li><el-button@click="changeWindowPosition({position: 'top-left',offsetX: Number.parseFloat(offsetX),offsetY: Number.parseFloat(offsetY),windowKey,})">左上</el-button><el-button@click="changeWindowPosition({position: 'top-right',offsetX: Number.parseFloat(offsetX),offsetY: Number.parseFloat(offsetY),windowKey,})">右上</el-button></li><li><el-button@click="changeWindowPosition({position: 'center',offsetX: Number.parseFloat(offsetX),offsetY: Number.parseFloat(offsetY),windowKey,})">居中</el-button></li><li><el-button@click="changeWindowPosition({position: 'bottom-left',offsetX: Number.parseFloat(offsetX),offsetY: Number.parseFloat(offsetY),windowKey,})">左下</el-button><el-button@click="changeWindowPosition({position: 'bottom-right',offsetX: Number.parseFloat(offsetX),offsetY: Number.parseFloat(offsetY),windowKey,})">右下</el-button></li></ul></div>
</template><script setup lang="ts">
import { changeWindowPosition } from "@/utils/electronUtils";
import { ref } from "vue";
const x = ref("0");
const y = ref("0");
const offsetX = ref("0");
const offsetY = ref("0");
const windowKey = ref("");
</script><style scoped>
ul {list-style: none;
}
</style>
测试效果
可以随心所欲的控制任何窗口到处定向移动了,是不是很酷 _三└(┐卍 ˘ω˘)卍
- 输入key可以操作指定窗口,不输入则操作本窗口
- 可以直接根据position参数快速控制窗口定向移动
- 输入偏移量微调窗口
相关文章:

electron+vue3全家桶+vite项目搭建【29】封装窗口工具类【3】控制窗口定向移动
文章目录 引入实现效果思路声明通用的定位对象主进程模块渲染进程测试效果 引入 demo项目地址 窗口工具类系列文章: 封装窗口工具类【1】雏形 封装窗口工具类【2】窗口组,维护窗口关系 封装窗口工具类【3】控制窗口定向移动 很多时候,我们想…...

深入了解304缓存原理:提升网站性能与加载速度
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...
python-批量操作excel
批量新增excel文件 import osimport xlwings as xwapp xw.App(visibleTrue,add_bookFalse)#visible设置为ture的时候会自动打开创建的excel文件,设为为false的时候不会看到excel文件打开了,实际进程占用了....dept_list [人事部,财务部,研发部,行政部…...

#QT(串口助手-界面)
1.IDE:QTCreator 2.实验:编写串口助手 3.记录 接收框:Plain Text Edit 属性选择:Combo Box 发送框:Line Edit 广告:Group Box (1)仿照现有串口助手设计UI界面 (2)此时串口助手大…...
C语言进阶——位段
在C语言中,位段(Bit Fields)是一种用来对结构体中的成员进行位级别的控制的特性。通过位段,我们可以灵活地控制结构体中各个成员的位数,从而节省内存空间并提高程序的效率。本篇博客将详细讲解C语言中位段的相关知识&a…...
软件设计师软考题目解析23 --每日五题
想说的话:要准备软考了。0.0,其实我是不想考的,但是吧,由于本人已经学完所有知识了,只是被学校的课程给锁在那里了,不然早找工作去了。寻思着反正也无聊,就考个证玩玩。 本人github地址…...
总结:前后端集合、数组类型数据交互底层原理,SpringBoot框架解析
总结:前后端集合、数组类型数据交互底层原理,SpringBoot框架解析 一前后端信息交互本质:1.两台电脑可以通过收发电磁波、控制网线电路开关等基础物理设施,就可以进行物理层面的电信号交互,电信号又可以通过各种传感设备…...

2024蓝桥杯每日一题(前缀和)
一、第一题:壁画 解题思路:前缀和贪心枚举 仔细思考可以发现B值最大的情况是一段连续的长度为n/2上取整的序列的累加和 【Python程序代码】 import math T int(input()) for _ in range(1,1T):n int(input())s input()l math.ceil(len(s)/…...

2007-2022年上市公司迪博内部控制评价缺陷数量数据
2007-2022年上市公司迪博内部控制评价缺陷数量数据 1、时间:2007-2022年 2、范围:上市公司 3、指标:证券代码、证券简称、辖区、证监会行业、申万行业、是否存在财报内控重大缺陷、财报内控重大缺陷数量、是否存在财报内控重要缺陷、财报内…...

JAVA虚拟机实战篇之内存调优[4](内存溢出问题案例)
文章目录 版权声明修复问题内存溢出问题分类 分页查询文章接口的内存溢出问题背景解决思路问题根源解决思路 Mybatis导致的内存溢出问题背景问题根源解决思路 导出大文件内存溢出问题背景问题根源解决思路 ThreadLocal占用大量内存问题背景问题根源解决思路 文章内容审核接口的…...

qt自定义时间选择控件窗口
效果如图: 布局如图: 参考代码: //DateTimeSelectWidget #ifndef DATETIMESELECTWIDGET_H #define DATETIMESELECTWIDGET_H#include <QWidget> #include <QDateTime>namespace Ui { class DateTimeSelectWidget; }class DateTim…...
如何不解压直接读取gzip文件里面的文件
要在服务器上不解压缩的情况下读取gzip文件中的文件内容,您可以使用类似于zlib模块的库,这些库允许您在内存中对gzip数据进行操作而无需解压缩到磁盘上的文件。 在Python中,您可以使用gzip模块来实现这一目的。以下是一个示例代码࿰…...

python 截取字符串string.split
目录 作用语法只要第一个值获得第3个值遍历 作用 根据某个符号对数据进行截取 从而获得自己想要的内容 语法 使用’string.split’ 方法 对字符串’123/abc/BPYC’ 以 ‘/’ 进行截取 string "123/abc/BPYC" substring string.split("/") print(subs…...

SpringBoot+Vue实现el-table表头筛选排序(附源码)
👨💻作者简介:在笑大学牲 🎟️个人主页:无所谓^_^ ps:点赞是免费的,却可以让写博客的作者开心好几天😎 前言 后台系统对table组件的需求是最常见的,不过element-ui的el…...

Linux学习之线程
目录 线程概念 1.什么是线程? 2.线程的优缺点 3.线程异常 4.线程用途 线程操作 1.如何给线程传参 2.线程终止 3.获取返回值 4.分离状态 5.退出线程 线程的用户级地址空间: 线程的局部存储 线程的同步与互斥 互斥量mutex 数据不一致的主要过…...

【JavaEE初阶】 JVM类加载简介
文章目录 🍃前言🌲类加载过程🚩加载🚩验证🚩准备🚩解析🚩初始化 🎄双亲委派模型🚩什么是双亲委派模型?🚩双亲委派模型的优点 ⭕总结 🍃…...
.NET Core依赖注入(IoC)不使用构造函数实现注入
在.NET Core中,依赖注入(IoC)通常是通过构造函数注入来实现的,这是推荐的方式,因为它使得依赖关系更加明确和可测试。但是,如果你不想或不能使用构造函数注入,你可以考虑使用方法注入࿰…...

WinSCP下载安装并结合内网穿透实现固定公网TCP地址访问本地服务器
文章目录 1. 简介2. 软件下载安装:3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件,它的主要功能是在本地与远程计…...

内联函数|auto关键字|范围for的语法|指针空值
文章目录 一、内联函数1.1概念1.2特性 二、auto关键字2.2类型别名思考2.3auto简介2.4auto使用细则2.4 auto不能推导的场景 三、基于范围的for循环(C11)3.1 范围for的语法 四、指针空值nullptr(C11)4.1 C98中的指针空值 所属专栏:C初阶 一、内联函数 1.1概念 以inline修饰的函…...

家用洗地机哪个型号好用?介绍几个值得考虑的品牌
作为家里的主要清洁工,我一直以来都是负责家里的清洁工作。我经常使用吸尘器和扫地机器人来轮流清洁,虽然效果还不错,但是这种方式太费时间和精力了。特别是在脸上厨房里做完饭和孩子吃完饭后留下的残渣时,我总是需要用传统的拖多…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...