electron-drag-plus 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/example.js CHANGED
@@ -2,38 +2,61 @@
2
2
  /**
3
3
  * electron-drag-plus 简化版API使用示例
4
4
  */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
5
8
  Object.defineProperty(exports, "__esModule", { value: true });
6
9
  // 主进程使用示例(main.js 或 main.ts)
7
10
  const electron_1 = require("electron");
8
- const index_1 = require("./index");
11
+ const index_1 = __importDefault(require("./index"));
9
12
  // 创建窗口
10
13
  const win = new electron_1.BrowserWindow({
11
14
  width: 800,
12
15
  height: 600,
13
- frame: false, // 无边框窗口
16
+ frame: false, // 无边框窗口通常需要自定义拖拽
14
17
  webPreferences: {
15
- nodeIntegration: true,
16
- contextIsolation: false
18
+ contextIsolation: false, // 确保能访问ipcRenderer
19
+ nodeIntegration: true
17
20
  }
18
21
  });
19
- // 初始化拖拽功能
20
- (0, index_1.initializeDragPlus)(win);
22
+ // 初始化拖拽功能 - 只需传递窗口实例
23
+ (0, index_1.default)(win);
21
24
  // 加载页面
22
25
  win.loadFile('index.html');
23
26
  // 渲染进程使用示例(renderer.js 或 renderer.ts)
24
- const renderer_1 = require("./renderer");
25
- // 为标题栏启用拖拽功能
26
- // 方法1:使用CSS选择器
27
- (0, renderer_1.enableWindowDrag)('#title-bar');
28
- // 方法2:使用DOM元素
29
- const titleBar = document.getElementById('title-bar');
30
- if (titleBar instanceof HTMLElement) {
31
- (0, renderer_1.enableWindowDrag)(titleBar);
32
- }
33
- // 禁用特定元素的拖拽
34
- const renderer_2 = require("./renderer");
35
- // 禁止按钮拖拽
36
- const closeButton = document.getElementById('close-button');
37
- if (closeButton instanceof HTMLElement) {
38
- (0, renderer_2.disableWindowDrag)(closeButton);
39
- }
27
+ // 在渲染进程文件中:
28
+ /*
29
+ import { enableWindowDrag, disableWindowDrag } from './renderer';
30
+
31
+ // DOM加载完成后调用
32
+ window.addEventListener('DOMContentLoaded', () => {
33
+ // 方式1:使用CSS选择器启用拖拽区域
34
+ enableWindowDrag('#title-bar');
35
+
36
+ // 方式2:直接传递DOM元素
37
+ const titleBar = document.getElementById('title-bar');
38
+ if (titleBar) {
39
+ enableWindowDrag(titleBar);
40
+ }
41
+
42
+ // 为按钮禁用拖拽,确保可以正常点击
43
+ const minimizeButton = document.getElementById('minimize-button');
44
+ const maximizeButton = document.getElementById('maximize-button');
45
+ const closeButton = document.getElementById('close-button');
46
+
47
+ if (minimizeButton) disableWindowDrag(minimizeButton);
48
+ if (maximizeButton) disableWindowDrag(maximizeButton);
49
+ if (closeButton) disableWindowDrag(closeButton);
50
+ });
51
+ */
52
+ // HTML示例
53
+ /*
54
+ <div id="title-bar" style="height: 30px; background: #333; color: white; display: flex; justify-content: space-between; align-items: center; padding: 0 10px;">
55
+ <div>窗口标题</div>
56
+ <div>
57
+ <button id="minimize-button">-</button>
58
+ <button id="maximize-button">□</button>
59
+ <button id="close-button">×</button>
60
+ </div>
61
+ </div>
62
+ */
package/dist/index.d.ts CHANGED
@@ -4,7 +4,4 @@ import { BrowserWindow } from 'electron';
4
4
  * @param win Electron窗口实例
5
5
  */
6
6
  export declare function initializeDragPlus(win: BrowserWindow): void;
7
- declare const _default: {
8
- initializeDragPlus: typeof initializeDragPlus;
9
- };
10
- export default _default;
7
+ export default initializeDragPlus;
package/dist/index.js CHANGED
@@ -2,8 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.initializeDragPlus = initializeDragPlus;
4
4
  const electron_1 = require("electron");
5
- // 当前活跃的窗口实例(不再需要Map存储)
6
- let activeWindowInstance = null;
5
+ // 存储当前拖拽的窗口实例
6
+ let currentWindow = null;
7
+ let dragData = null;
7
8
  /**
8
9
  * 主进程初始化拖拽功能
9
10
  * @param win Electron窗口实例
@@ -12,66 +13,60 @@ function initializeDragPlus(win) {
12
13
  if (!win) {
13
14
  throw new Error('窗口实例不能为空');
14
15
  }
15
- const webContents = win.webContents;
16
- // 设置为当前活跃窗口实例
17
- activeWindowInstance = { window: win, webContents };
18
- // 清理函数
19
- const cleanup = () => {
20
- if (activeWindowInstance?.window === win) {
21
- activeWindowInstance = null;
16
+ // 监听窗口关闭事件清理资源
17
+ win.on('closed', () => {
18
+ if (currentWindow === win) {
19
+ currentWindow = null;
20
+ dragData = null;
22
21
  }
23
- };
24
- // 监听窗口关闭事件进行清理
25
- win.on('closed', cleanup);
22
+ });
26
23
  }
27
- // 处理拖拽事件
28
- electron_1.ipcMain.on('drag-plus:start', (event, { x, y }) => {
29
- if (!activeWindowInstance)
24
+ // 监听拖拽开始事件
25
+ electron_1.ipcMain.on('electron-drag:start', (event, { x, y }) => {
26
+ // 获取触发事件的窗口
27
+ const win = electron_1.BrowserWindow.fromWebContents(event.sender);
28
+ if (!win)
30
29
  return;
31
- const { window } = activeWindowInstance;
32
- const data = {
30
+ // 设置当前窗口和拖拽数据
31
+ currentWindow = win;
32
+ dragData = {
33
33
  isDragging: true,
34
34
  startX: x,
35
35
  startY: y,
36
- originalResizable: window.isResizable()
36
+ originalResizable: win.isResizable()
37
37
  };
38
- // 关联拖拽状态到窗口实例
39
- activeWindowInstance.dragData = data;
38
+ // 临时禁用窗口大小调整以优化拖拽体验
39
+ win.setResizable(false);
40
40
  });
41
- electron_1.ipcMain.on('drag-plus:drag', (event, { x, y }) => {
42
- if (!activeWindowInstance || !activeWindowInstance.dragData?.isDragging)
41
+ // 监听拖拽移动事件
42
+ electron_1.ipcMain.on('electron-drag:drag', (event, { x, y }) => {
43
+ if (!currentWindow || !dragData || !dragData.isDragging)
43
44
  return;
44
- const { window, dragData } = activeWindowInstance;
45
- const winBounds = window.getBounds();
46
- // 计算新位置
47
- const newX = winBounds.x + (x - dragData.startX);
48
- const newY = winBounds.y + (y - dragData.startY);
49
- // 应用窗口防抖优化
50
- // 1. 记住当前尺寸
51
- const size = window.getSize();
52
- // 2. 关闭缩放
53
- window.setResizable(false);
54
- // 3. 设置新坐标
55
- window.setPosition(newX, newY, false);
56
- // 4. 强制写回尺寸
57
- window.setSize(size[0], size[1], false);
58
- // 更新拖拽起始位置
59
- dragData.startX = x;
60
- dragData.startY = y;
45
+ try {
46
+ const winBounds = currentWindow.getBounds();
47
+ // 计算新位置
48
+ const newX = winBounds.x + (x - dragData.startX);
49
+ const newY = winBounds.y + (y - dragData.startY);
50
+ // 设置窗口位置
51
+ currentWindow.setPosition(newX, newY, false);
52
+ // 更新拖拽起始位置
53
+ dragData.startX = x;
54
+ dragData.startY = y;
55
+ }
56
+ catch (error) {
57
+ console.error('拖拽移动出错:', error);
58
+ }
61
59
  });
62
- electron_1.ipcMain.on('drag-plus:stop', () => {
63
- if (!activeWindowInstance || !activeWindowInstance.dragData)
60
+ // 监听拖拽结束事件
61
+ electron_1.ipcMain.on('electron-drag:stop', () => {
62
+ if (!currentWindow || !dragData)
64
63
  return;
65
- const { window, dragData } = activeWindowInstance;
66
- // 恢复原始的可缩放状态
64
+ // 恢复窗口原始大小调整状态
67
65
  if (dragData.originalResizable !== undefined) {
68
- window.setResizable(dragData.originalResizable);
66
+ currentWindow.setResizable(dragData.originalResizable);
69
67
  }
70
- // 清理拖拽数据
71
- delete activeWindowInstance.dragData;
68
+ // 清理拖拽状态
69
+ dragData = null;
72
70
  });
73
- // 确保Map类型正确
74
71
  // 默认导出
75
- exports.default = {
76
- initializeDragPlus
77
- };
72
+ exports.default = initializeDragPlus;
@@ -1,15 +1,15 @@
1
1
  /**
2
- * 渲染进程启用拖拽功能
3
- * @param element 元素或元素选择器
2
+ * 为DOM元素启用窗口拖拽功能
3
+ * @param selector CSS选择器或HTMLElement对象
4
4
  */
5
- export declare function enableWindowDrag(element: HTMLElement | string): void;
5
+ export declare function enableWindowDrag(selector: string | HTMLElement): void;
6
6
  /**
7
- * 渲染进程禁用拖拽功能
8
- * @param element 元素或元素选择器
7
+ * 禁用DOM元素的窗口拖拽功能
8
+ * @param selector CSS选择器或HTMLElement对象
9
9
  */
10
- export declare function disableWindowDrag(element: HTMLElement | string): void;
10
+ export declare function disableWindowDrag(selector: string | HTMLElement): void;
11
11
  declare const _default: {
12
- enableDrag: typeof enableWindowDrag;
13
- disableDrag: typeof disableWindowDrag;
12
+ enableWindowDrag: typeof enableWindowDrag;
13
+ disableWindowDrag: typeof disableWindowDrag;
14
14
  };
15
15
  export default _default;
package/dist/renderer.js CHANGED
@@ -7,150 +7,81 @@ const electron_1 = require("electron");
7
7
  let isDragging = false;
8
8
  let dragStartX = 0;
9
9
  let dragStartY = 0;
10
- // 初始化标志
11
- let isInitialized = false;
12
- // 初始化
13
- function initDragSystem() {
14
- // 如果已经初始化,不再重复初始化
15
- if (isInitialized)
16
- return;
17
- try {
18
- // 设置全局事件监听器 - 移除key相关的逻辑
19
- document.addEventListener('mousedown', handleMouseDown, true);
20
- document.addEventListener('mousemove', handleMouseMove, true);
21
- document.addEventListener('mouseup', handleMouseUp, true);
22
- isInitialized = true;
23
- // 清理函数
24
- return () => {
25
- document.removeEventListener('mousedown', handleMouseDown, true);
26
- document.removeEventListener('mousemove', handleMouseMove, true);
27
- document.removeEventListener('mouseup', handleMouseUp, true);
28
- isInitialized = false;
29
- };
30
- }
31
- catch (error) {
32
- console.error('初始化拖拽系统失败:', error);
33
- // 返回一个空的清理函数,避免在调用时出错
34
- return () => { };
35
- }
10
+ // 初始化拖拽事件系统
11
+ function initDragEvents() {
12
+ // 设置全局鼠标事件监听器
13
+ document.addEventListener('mousedown', handleMouseDown, true);
14
+ document.addEventListener('mousemove', handleMouseMove, true);
15
+ document.addEventListener('mouseup', handleMouseUp, true);
16
+ // 清理函数
17
+ return () => {
18
+ document.removeEventListener('mousedown', handleMouseDown, true);
19
+ document.removeEventListener('mousemove', handleMouseMove, true);
20
+ document.removeEventListener('mouseup', handleMouseUp, true);
21
+ };
36
22
  }
37
- // 延迟初始化,避免在DOM未准备好时初始化
38
- function delayedInit() {
39
- if (document.readyState === 'loading') {
40
- document.addEventListener('DOMContentLoaded', () => {
41
- // 保存返回的清理函数
42
- const cleanup = initDragSystem();
43
- if (cleanup) {
44
- cleanupFunction = cleanup;
45
- }
46
- });
47
- }
48
- else {
49
- // 保存返回的清理函数
50
- const cleanup = initDragSystem();
51
- if (cleanup) {
52
- cleanupFunction = cleanup;
23
+ // 初始化系统
24
+ let cleanupFunction = null;
25
+ if (typeof document !== 'undefined') {
26
+ cleanupFunction = initDragEvents();
27
+ // 页面卸载时清理
28
+ window.addEventListener('beforeunload', () => {
29
+ if (cleanupFunction) {
30
+ cleanupFunction();
53
31
  }
54
- }
55
- }
56
- // 自动初始化,但添加错误处理和延迟
57
- let cleanupFunction = () => { };
58
- try {
59
- delayedInit();
60
- }
61
- catch (error) {
62
- console.warn('初始化拖拽系统失败:', error);
32
+ });
63
33
  }
64
- // 页面卸载时清理
65
- window.addEventListener('beforeunload', () => {
66
- if (cleanupFunction) {
67
- cleanupFunction();
68
- }
69
- });
70
34
  // 鼠标按下事件处理器
71
35
  function handleMouseDown(e) {
72
- try {
73
- const target = e.target;
74
- if (!target)
75
- return;
76
- // 检查元素是否是可拖拽区域
77
- if (target.style.appRegion === 'drag' && !isElementOrParentNoDrag(target)) {
78
- isDragging = true;
79
- dragStartX = e.screenX;
80
- dragStartY = e.screenY;
81
- // 通知主进程开始拖拽,移除key参数
82
- try {
83
- electron_1.ipcRenderer.send('drag-plus:start', {
84
- x: e.screenX,
85
- y: e.screenY
86
- });
87
- }
88
- catch (error) {
89
- console.warn('发送拖拽开始事件失败:', error);
90
- // 重置拖拽状态
91
- isDragging = false;
92
- }
93
- }
94
- }
95
- catch (error) {
96
- console.error('处理鼠标按下事件失败:', error);
97
- isDragging = false;
36
+ const target = e.target;
37
+ if (!target)
38
+ return;
39
+ // 检查元素是否是可拖拽区域且不是no-drag区域
40
+ if (target.style.appRegion === 'drag' && !isElementNoDrag(target)) {
41
+ isDragging = true;
42
+ dragStartX = e.screenX;
43
+ dragStartY = e.screenY;
44
+ // 通知主进程开始拖拽
45
+ electron_1.ipcRenderer.send('electron-drag:start', {
46
+ x: e.screenX,
47
+ y: e.screenY
48
+ });
98
49
  }
99
50
  }
100
51
  // 鼠标移动事件处理器
101
52
  function handleMouseMove(e) {
102
- // 确保我们在拖拽状态
103
53
  if (!isDragging)
104
54
  return;
105
- try {
106
- // 计算鼠标移动距离
107
- const deltaX = e.screenX - dragStartX;
108
- const deltaY = e.screenY - dragStartY;
109
- // 只有在移动超过一定阈值时才发送拖拽事件,减少抖动
110
- if (Math.abs(deltaX) > 2 || Math.abs(deltaY) > 2) {
111
- try {
112
- // 移除key参数
113
- electron_1.ipcRenderer.send('drag-plus:drag', {
114
- x: e.screenX,
115
- y: e.screenY
116
- });
117
- // 更新拖拽起始位置
118
- dragStartX = e.screenX;
119
- dragStartY = e.screenY;
120
- }
121
- catch (error) {
122
- console.warn('发送拖拽移动事件失败:', error);
123
- // 重置拖拽状态
124
- isDragging = false;
125
- }
126
- }
127
- }
128
- catch (error) {
129
- console.error('处理鼠标移动事件失败:', error);
130
- isDragging = false;
55
+ // 计算鼠标移动距离
56
+ const deltaX = e.screenX - dragStartX;
57
+ const deltaY = e.screenY - dragStartY;
58
+ // 只有在移动超过一定阈值时才发送拖拽事件,减少抖动
59
+ if (Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1) {
60
+ // 通知主进程进行拖拽
61
+ electron_1.ipcRenderer.send('electron-drag:drag', {
62
+ x: e.screenX,
63
+ y: e.screenY
64
+ });
65
+ // 更新拖拽起始位置
66
+ dragStartX = e.screenX;
67
+ dragStartY = e.screenY;
131
68
  }
132
69
  }
133
70
  // 鼠标释放事件处理器
134
71
  function handleMouseUp() {
135
- // 确保我们在拖拽状态
136
- if (!isDragging) {
72
+ if (!isDragging)
137
73
  return;
138
- }
139
- // 先重置拖拽状态,避免事件循环
74
+ // 重置拖拽状态
140
75
  isDragging = false;
141
- // 通知主进程结束拖拽,移除key参数
142
- try {
143
- electron_1.ipcRenderer.send('drag-plus:stop');
144
- }
145
- catch (error) {
146
- console.warn('发送拖拽停止事件失败:', error);
147
- }
76
+ // 通知主进程结束拖拽
77
+ electron_1.ipcRenderer.send('electron-drag:stop');
148
78
  }
149
- // 检查元素或其父元素是否有no-drag样式
150
- function isElementOrParentNoDrag(element) {
79
+ // 检查元素是否是不可拖拽区域
80
+ function isElementNoDrag(element) {
151
81
  if (element.style.appRegion === 'no-drag') {
152
82
  return true;
153
83
  }
84
+ // 检查父元素链
154
85
  let parent = element.parentElement;
155
86
  while (parent) {
156
87
  if (parent.style.appRegion === 'no-drag') {
@@ -161,101 +92,55 @@ function isElementOrParentNoDrag(element) {
161
92
  return false;
162
93
  }
163
94
  /**
164
- * 渲染进程启用拖拽功能
165
- * @param element 元素或元素选择器
95
+ * 为DOM元素启用窗口拖拽功能
96
+ * @param selector CSS选择器或HTMLElement对象
166
97
  */
167
- function enableWindowDrag(element) {
168
- try {
169
- // 确保在DOM环境中执行
170
- if (typeof document === 'undefined' || !document.querySelector) {
171
- console.warn('无法在当前环境中执行此方法: 缺少DOM支持');
172
- return;
173
- }
174
- // 获取目标元素
175
- const target = typeof element === 'string'
176
- ? document.querySelector(element)
177
- : element;
178
- if (!target || !(target instanceof HTMLElement)) {
179
- console.warn('元素不存在或不是有效的HTMLElement');
180
- return;
181
- }
182
- // 安全地设置拖拽样式
183
- try {
184
- // 先设置CSS样式,这应该不会影响窗口显示
185
- target.style.userSelect = 'none';
186
- target.style.webkitAppRegion = 'drag';
187
- target.style.appRegion = 'drag';
188
- }
189
- catch (styleError) {
190
- console.error('设置元素样式失败:', styleError);
191
- // 继续执行,不要中断函数
192
- }
193
- // 自动为子交互元素设置no-drag,但不要因为这个过程的异常而中断
194
- try {
195
- const interactiveElements = target.querySelectorAll('button, input, select, textarea, a, [onclick], [href], [tabindex]:not([tabindex="-1"])');
196
- // 分批处理,避免大量元素导致性能问题
197
- const batchSize = 50;
198
- for (let i = 0; i < interactiveElements.length; i += batchSize) {
199
- const batch = Array.from(interactiveElements).slice(i, i + batchSize);
200
- // 使用setTimeout避免阻塞主线程
201
- setTimeout(() => {
202
- batch.forEach(child => {
203
- try {
204
- if (child instanceof HTMLElement && !child.classList.contains('ignore-no-drag')) {
205
- child.style.userSelect = 'none';
206
- child.style.webkitAppRegion = 'no-drag';
207
- child.style.appRegion = 'no-drag';
208
- }
209
- }
210
- catch (itemError) {
211
- // 忽略单个元素的错误,继续处理其他元素
212
- }
213
- });
214
- }, 0);
215
- }
216
- }
217
- catch (error) {
218
- console.warn('设置子交互元素失败:', error);
219
- // 不中断主函数
220
- }
221
- }
222
- catch (error) {
223
- console.error('启用窗口拖拽失败:', error);
224
- // 最外层捕获,确保即使发生严重错误也不会影响窗口显示
98
+ function enableWindowDrag(selector) {
99
+ // 确保在DOM环境中执行
100
+ if (typeof document === 'undefined')
101
+ return;
102
+ // 获取目标元素
103
+ const element = typeof selector === 'string'
104
+ ? document.querySelector(selector)
105
+ : selector;
106
+ if (!element || !(element instanceof HTMLElement)) {
107
+ return;
225
108
  }
109
+ // 设置拖拽区域样式
110
+ element.style.userSelect = 'none';
111
+ element.style.webkitAppRegion = 'drag';
112
+ element.style.appRegion = 'drag';
113
+ // 为交互元素设置不可拖拽,确保正常操作
114
+ const interactiveElements = element.querySelectorAll('button, input, select, textarea, a, [onclick], [href], [tabindex]:not([tabindex="-1"])');
115
+ interactiveElements.forEach(child => {
116
+ if (child instanceof HTMLElement) {
117
+ child.style.userSelect = 'none';
118
+ child.style.webkitAppRegion = 'no-drag';
119
+ child.style.appRegion = 'no-drag';
120
+ }
121
+ });
226
122
  }
227
123
  /**
228
- * 渲染进程禁用拖拽功能
229
- * @param element 元素或元素选择器
124
+ * 禁用DOM元素的窗口拖拽功能
125
+ * @param selector CSS选择器或HTMLElement对象
230
126
  */
231
- function disableWindowDrag(element) {
232
- try {
233
- // 确保在DOM环境中执行
234
- if (typeof document === 'undefined' || !document.querySelector) {
235
- return;
236
- }
237
- const target = typeof element === 'string'
238
- ? document.querySelector(element)
239
- : element;
240
- if (!target || !(target instanceof HTMLElement)) {
241
- return;
242
- }
243
- // 安全地设置为不可拖拽区域
244
- try {
245
- target.style.webkitAppRegion = 'no-drag';
246
- target.style.appRegion = 'no-drag';
247
- }
248
- catch (styleError) {
249
- console.error('设置元素不可拖拽样式失败:', styleError);
250
- }
251
- }
252
- catch (error) {
253
- console.error('禁用窗口拖拽失败:', error);
254
- // 最外层捕获,确保即使发生严重错误也不会影响窗口显示
127
+ function disableWindowDrag(selector) {
128
+ // 确保在DOM环境中执行
129
+ if (typeof document === 'undefined')
130
+ return;
131
+ // 获取目标元素
132
+ const element = typeof selector === 'string'
133
+ ? document.querySelector(selector)
134
+ : selector;
135
+ if (!element || !(element instanceof HTMLElement)) {
136
+ return;
255
137
  }
138
+ // 设置为不可拖拽区域
139
+ element.style.webkitAppRegion = 'no-drag';
140
+ element.style.appRegion = 'no-drag';
256
141
  }
257
142
  // 默认导出
258
143
  exports.default = {
259
- enableDrag: enableWindowDrag,
260
- disableDrag: disableWindowDrag
144
+ enableWindowDrag,
145
+ disableWindowDrag
261
146
  };
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "electron-drag-plus",
3
- "version": "1.0.2",
4
- "description": "增强版Electron窗口拖拽库,解决窗口抖动、不同分辨率/缩放导致的闪屏问题,并支持拖拽区域忽略鼠标事件",
3
+ "version": "1.0.3",
4
+ "description": "简单高效的Electron窗口拖拽库,支持自定义拖拽区域和交互元素,适用于无边框窗口应用",
5
5
  "main": "dist/index.js",
6
+ "browser": "dist/renderer.js",
6
7
  "types": "dist/index.d.ts",
7
8
  "scripts": {
8
9
  "build": "tsc",