electron-drag-plus
v1.1.0
Published
简单高效的Electron窗口拖拽库,支持自定义拖拽区域和交互元素,适用于无边框窗口应用
Downloads
49
Maintainers
Readme
Electron Drag Plus
一个极简高效的 Electron 窗口拖拽增强库,为无边框窗口提供流畅的自定义标题栏拖拽体验,内置窗口防抖优化和智能交互元素处理。
🚀 特性
- 极简 API 设计:主进程一键初始化,渲染进程仅需两个核心方法
- 智能元素处理:自动识别并排除常见交互元素(按钮、输入框等),避免拖拽冲突
- 窗口防抖优化:内置防抖机制,彻底解决拖拽时窗口大小抖动问题
- 完整 TypeScript 支持:提供精确的类型定义,支持智能提示
- 安全可靠:内置错误处理和延迟初始化,确保在各种环境下稳定运行
- 跨平台兼容:支持 Windows、macOS 和 Linux
📦 安装
# 使用 npm
npm install electron-drag-plus
# 使用 yarn
yarn add electron-drag-plus
# 使用 pnpm
pnpm add electron-drag-plus📖 API 文档
主进程 API
initializeDragPlus(window, callbacks?)
初始化窗口拖拽功能,为指定的 Electron 窗口启用拖拽支持。
参数:
window: BrowserWindow- Electron 窗口实例callbacks?: DragCallbacks- 可选的拖拽事件回调函数对象,包含:onDragStart?: (window: BrowserWindow) => void- 拖拽开始时的回调函数onDragEnd?: (window: BrowserWindow) => void- 拖拽结束时的回调函数
返回值: void
使用示例 - 基本用法:
import { BrowserWindow } from 'electron';
import { initializeDragPlus } from 'electron-drag-plus';
// 创建无边框窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
frame: false, // 无边框窗口
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload: path.join(__dirname, 'preload.js')
}
});
// 初始化拖拽功能
initializeDragPlus(mainWindow);使用示例 - 带回调函数:
import { BrowserWindow } from 'electron';
import { initializeDragPlus } from 'electron-drag-plus';
// 创建无边框窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
frame: false,
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload: path.join(__dirname, 'preload.js')
}
});
// 初始化拖拽功能并添加回调
initializeDragPlus(mainWindow, {
onDragStart: (window) => {
console.log('窗口开始拖拽');
// 可以在这里执行其他操作,如记录拖拽开始位置、修改UI状态等
},
onDragEnd: (window) => {
console.log('窗口拖拽结束');
// 可以在这里执行其他操作,如保存窗口位置、触发动画效果等
}
});渲染进程 API
enableWindowDrag(selector)
启用指定 DOM 元素或选择器的窗口拖拽功能。
参数:
selector: HTMLElement | string- DOM 元素对象或 CSS 选择器字符串
返回值: boolean - 操作是否成功
使用示例:
import { enableWindowDrag } from 'electron-drag-plus/renderer';
// 使用 CSS 选择器启用拖拽
const success = enableWindowDrag('#title-bar');
// 或使用 DOM 元素引用
const titleBar = document.getElementById('title-bar');
if (titleBar instanceof HTMLElement) {
enableWindowDrag(titleBar);
}disableWindowDrag(selector)
禁用指定 DOM 元素或选择器的窗口拖拽功能,使元素可正常交互。
参数:
selector: HTMLElement | string- DOM 元素对象或 CSS 选择器字符串
返回值: boolean - 操作是否成功
使用示例:
import { disableWindowDrag } from 'electron-drag-plus/renderer';
// 禁用按钮的拖拽,使其可以正常点击
const closeButton = document.getElementById('close-button');
if (closeButton instanceof HTMLElement) {
disableWindowDrag(closeButton);
}📝 完整示例
主进程 (main.js)
const { app, BrowserWindow } = require('electron');
const path = require('path');
const { initializeDragPlus } = require('electron-drag-plus');
function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
frame: false, // 无边框窗口
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // 推荐启用
nodeIntegration: false // 推荐禁用
}
});
// 初始化拖拽功能
initializeDragPlus(mainWindow, {
onDragStart: (window) => {
console.log('窗口开始拖拽');
},
onDragEnd: (window) => {
console.log('窗口拖拽结束');
// 可以在这里保存窗口位置等操作
}
});
// 加载应用
mainWindow.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
// macOS 特定处理
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
// 保留 macOS 的标准行为
if (process.platform !== 'darwin') {
app.quit();
}
});渲染进程 (renderer.js)
import { enableWindowDrag, disableWindowDrag } from 'electron-drag-plus/renderer';
// 等待 DOM 加载完成
document.addEventListener('DOMContentLoaded', () => {
// 启用标题栏拖拽
const titleBar = document.getElementById('title-bar');
if (titleBar) {
enableWindowDrag(titleBar);
console.log('标题栏拖拽已启用');
}
// 禁用控制按钮的拖拽
const minimizeButton = document.getElementById('minimize-button');
const maximizeButton = document.getElementById('maximize-button');
const closeButton = document.getElementById('close-button');
if (minimizeButton) disableWindowDrag(minimizeButton);
if (maximizeButton) disableWindowDrag(maximizeButton);
if (closeButton) disableWindowDrag(closeButton);
// 添加窗口控制事件
minimizeButton?.addEventListener('click', () => {
// 此处需要通过 IPC 或预加载 API 调用主进程方法来最小化窗口
window.electron?.minimizeWindow();
});
maximizeButton?.addEventListener('click', () => {
window.electron?.maximizeWindow();
});
closeButton?.addEventListener('click', () => {
window.electron?.closeWindow();
});
});HTML 结构示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Electron 自定义窗口示例</title>
<style>
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* 自定义标题栏 */
#title-bar {
height: 32px;
background-color: #252525;
color: white;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
-webkit-app-region: drag; /* 原生拖拽属性(将被我们的库覆盖) */
}
#title {
font-size: 14px;
font-weight: 500;
}
/* 窗口控制按钮 */
.window-controls {
display: flex;
gap: 8px;
-webkit-app-region: no-drag; /* 防止控制区域被拖拽 */
}
.window-control-btn {
width: 24px;
height: 24px;
border: none;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 0.2s ease;
}
.window-control-btn:hover {
opacity: 0.9;
}
#minimize-button {
background-color: transparent;
color: #cccccc;
}
#minimize-button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#maximize-button {
background-color: transparent;
color: #cccccc;
}
#maximize-button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#close-button {
background-color: #ff5f56;
color: white;
}
/* 应用内容区域 */
#content {
padding: 20px;
}
</style>
</head>
<body>
<div id="title-bar">
<div id="title">Electron Drag Plus 示例</div>
<div class="window-controls">
<button id="minimize-button">−</button>
<button id="maximize-button">◻</button>
<button id="close-button">×</button>
</div>
</div>
<div id="content">
<h1>欢迎使用 Electron Drag Plus</h1>
<p>您可以通过拖动上方的标题栏来移动窗口</p>
<p>窗口控制按钮不会被拖动功能影响,可以正常点击</p>
</div>
<script src="renderer.js"></script>
</body>
</html>预加载脚本示例 (preload.js)
const { contextBridge, ipcRenderer } = require('electron');
// 暴露窗口控制 API 到渲染进程
contextBridge.exposeInMainWorld('electron', {
minimizeWindow: () => ipcRenderer.send('window:minimize'),
maximizeWindow: () => ipcRenderer.send('window:maximize'),
closeWindow: () => ipcRenderer.send('window:close')
});
// 主进程需要监听这些事件
/*
ipcMain.on('window:minimize', () => {
const window = BrowserWindow.getFocusedWindow();
if (window) window.minimize();
});
ipcMain.on('window:maximize', () => {
const window = BrowserWindow.getFocusedWindow();
if (window) {
if (window.isMaximized()) {
window.unmaximize();
} else {
window.maximize();
}
}
});
ipcMain.on('window:close', () => {
const window = BrowserWindow.getFocusedWindow();
if (window) window.close();
});
*/🛡️ 安全最佳实践
启用 Context Isolation
- 在创建窗口时,始终设置
contextIsolation: true和nodeIntegration: false - 通过预加载脚本安全地暴露必要的 API
- 在创建窗口时,始终设置
处理 DOM 就绪状态
- 在渲染进程中,确保在调用
enableWindowDrag或disableWindowDrag前,DOM 已经完全加载 - 使用
DOMContentLoaded事件监听器确保代码在适当的时机执行
- 在渲染进程中,确保在调用
错误处理
- 始终检查元素是否存在,避免对不存在的元素调用方法
- 考虑使用
try/catch块处理可能的异常
🔄 拖拽事件回调机制
从 v1.0.13 版本开始,库支持拖拽事件回调功能,允许开发者在拖拽过程中的关键节点执行自定义操作:
- 拖拽开始回调 - 当用户开始拖拽窗口时触发
- 拖拽结束回调 - 当用户结束拖拽窗口时触发
回调机制可以用于多种场景,如:
- 记录和保存窗口位置历史
- 在拖拽过程中显示特殊UI效果
- 实现拖拽状态的自定义动画
- 触发与窗口位置相关的业务逻辑
💡 窗口防抖机制说明
Electron Drag Plus 实现了智能的窗口防抖机制,彻底解决了在 Windows 平台上拖拽无边框窗口时出现的窗口大小抖动问题:
- 临时禁用窗口调整:拖拽开始时保存窗口原始可调整大小状态,并临时禁用调整
- 强制保持窗口尺寸:在拖拽过程中主动维持窗口尺寸不变
- 智能恢复状态:拖拽结束后,无缝恢复窗口原始的可调整大小状态
🌐 浏览器兼容性
从 v1.0.4 版本开始,库增加了更好的环境检测和错误处理:
- 安全的Electron API导入 - 仅在检测到Electron环境时才尝试导入ipcRenderer
- 优雅降级 - 在非Electron环境中不会抛出错误,而是静默跳过Electron特有的功能
- 全局变量安全检查 - 所有使用document、window等浏览器API前都会进行存在性检查
- 避免使用Node.js特有全局变量 - 移除了对__dirname等Node.js特有变量的依赖
这使得库可以在现代前端框架(如Vue、React等)中安全使用,即使在开发环境的浏览器中也不会导致构建或运行时错误。
🔧 兼容性要求
- Electron:13.x 及以上版本
- TypeScript:4.0+(如使用 TypeScript)
- 平台支持:Windows、macOS、Linux
- 现代浏览器:支持ES2020的浏览器环境(用于开发环境)
💻 安装和使用建议
在Vue/React项目中使用
在渲染进程中,推荐在组件挂载后条件性地使用库,以避免在非Electron环境中出现问题:
// Vue示例
import { onMounted } from 'vue';
import { enableWindowDrag } from 'electron-drag-plus/renderer';
onMounted(() => {
// 检查是否在Electron环境中
if (window.process?.versions?.electron) {
enableWindowDrag('.title-bar');
}
});📄 许可证
🤝 贡献
欢迎提交 Issues 和 Pull Requests!如果您发现了问题或有改进建议,请随时提出。
