npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

electron-drag-plus

v1.1.0

Published

简单高效的Electron窗口拖拽库,支持自定义拖拽区域和交互元素,适用于无边框窗口应用

Downloads

49

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();
});
*/

🛡️ 安全最佳实践

  1. 启用 Context Isolation

    • 在创建窗口时,始终设置 contextIsolation: truenodeIntegration: false
    • 通过预加载脚本安全地暴露必要的 API
  2. 处理 DOM 就绪状态

    • 在渲染进程中,确保在调用 enableWindowDragdisableWindowDrag 前,DOM 已经完全加载
    • 使用 DOMContentLoaded 事件监听器确保代码在适当的时机执行
  3. 错误处理

    • 始终检查元素是否存在,避免对不存在的元素调用方法
    • 考虑使用 try/catch 块处理可能的异常

🔄 拖拽事件回调机制

从 v1.0.13 版本开始,库支持拖拽事件回调功能,允许开发者在拖拽过程中的关键节点执行自定义操作:

  1. 拖拽开始回调 - 当用户开始拖拽窗口时触发
  2. 拖拽结束回调 - 当用户结束拖拽窗口时触发

回调机制可以用于多种场景,如:

  • 记录和保存窗口位置历史
  • 在拖拽过程中显示特殊UI效果
  • 实现拖拽状态的自定义动画
  • 触发与窗口位置相关的业务逻辑

💡 窗口防抖机制说明

Electron Drag Plus 实现了智能的窗口防抖机制,彻底解决了在 Windows 平台上拖拽无边框窗口时出现的窗口大小抖动问题:

  1. 临时禁用窗口调整:拖拽开始时保存窗口原始可调整大小状态,并临时禁用调整
  2. 强制保持窗口尺寸:在拖拽过程中主动维持窗口尺寸不变
  3. 智能恢复状态:拖拽结束后,无缝恢复窗口原始的可调整大小状态

🌐 浏览器兼容性

从 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');
  }
});

📄 许可证

MIT License

🤝 贡献

欢迎提交 Issues 和 Pull Requests!如果您发现了问题或有改进建议,请随时提出。

🔗 更多资源