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 🙏

© 2026 – Pkg Stats / Ryan Hefner

use-canvas-drag

v0.6.0

Published

Vue3 画布拖拽组合式函数,支持多种触发方式

Readme

use-canvas-drag

npm npm GitHub TypeScript

Vue3 画布拖拽组合式函数,轻量、无依赖、TypeScript 支持,开箱即用。

特性

  • 🌿 轻量无依赖 - 仅 ~1KB,零外部依赖
  • 🔧 多种触发方式 - 支持右键、中键、左键 + 修饰键组合
  • ⌨️ 独立方向键模式 - 简单配置 arrowKeys 即可启用上下左右微调
  • 📦 开箱即用 - 组合式函数设计,天然 Tree-Shakable
  • 🔰 TypeScript - 完整类型提示,IDE 友好
  • 🎯 零外部依赖 - 不依赖任何 UI 框架

安装

npm install use-canvas-drag
# 或
pnpm add use-canvas-drag
# 或
yarn add use-canvas-drag

推荐用法

推荐使用修饰键组合来避免误触发拖拽,这样用户可以正常在画布上进行其他操作(如选中元素),只有在按住修饰键时才触发拖拽:

<template>
  <div
    id="canvas"
    ref="canvasRef"
    class="canvas"
    @mousedown="handlers.onMouseDown"
    @contextmenu="handlers.onContextMenu"
    @keydown="handlers.onKeyDown"
  >
    <!-- 画布内容 -->
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useCanvasDrag } from 'use-canvas-drag'

const canvasRef = ref(null)

const { handlers, isPanning } = useCanvasDrag({
  container: () => canvasRef.value,
  // 推荐:Shift + 左键 或 Shift + 右键 拖拽
  // 光标保持默认,用户可正常操作画布
  // 修饰键大小写不敏感:'shift+left' 和 'Shift+Left' 效果相同
  mode: ['shift+left', 'shift+right'],
  arrowKeys: true,
  onDrag: ({ x, y }) => {
    console.log('拖拽距离:', x, y)
  }
})
</script>

<style scoped>
.canvas {
  width: 100%;
  height: 500px;
  border: 1px solid #ddd;
  overflow: auto;
  position: relative;
}
/* 无需手动设置 cursor,插件会自动处理 */
</style>

优势:

  • 🖱️ 按住 Shift + 鼠标左右键均可拖拽
  • 🎯 光标保持默认,不影响画布内的其他交互
  • ⌨️ 可配合 arrowKeys 实现键盘微调

快速开始

<template>
  <div
    id="canvas"
    ref="canvasRef"
    class="canvas"
    @mousedown="handlers.onMouseDown"
    @contextmenu="handlers.onContextMenu"
    @keydown="handlers.onKeyDown"
  >
    <div class="content">
      <div class="item">拖拽滚动画布</div>
      <div class="item">拖拽滚动画布</div>
      <div class="item">拖拽滚动画布</div>
      <div class="item">拖拽滚动画布</div>
      <div class="item">拖拽滚动画布</div>
      <div class="item">拖拽滚动画布</div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useCanvasDrag } from 'use-canvas-drag'

const canvasRef = ref(null)

const { handlers, isPanning } = useCanvasDrag({
  container: () => canvasRef.value,
  mode: 'left',
  onDrag: ({ x, y }) => {
    console.log('拖拽距离:', x, y)
  }
})
</script>

<style scoped>
.canvas {
  width: 100%;
  height: 500px;
  border: 1px solid #ddd;
  overflow: auto;
  position: relative;
  cursor: grab;
}
.canvas:active {
  cursor: grabbing;
}
.content {
  width: 1200px;
  height: 800px;
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  padding: 20px;
}
.item {
  width: 200px;
  height: 150px;
  background: #4a90e2;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
}
</style>

鼠标拖拽 + 方向键混合示例

<template>
  <div
    id="canvas"
    ref="canvasRef"
    class="canvas"
    @mousedown="handlers.onMouseDown"
    @contextmenu="handlers.onContextMenu"
    @keydown="handlers.onKeyDown"
  >
    <!-- 画布内容 -->
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useCanvasDrag } from 'use-canvas-drag'

const canvasRef = ref(null)

const { handlers, isPanning } = useCanvasDrag({
  container: () => canvasRef.value,
  mode: 'left',           // 左键拖拽
  arrowKeys: true,        // 启用方向键微调
  onStartDrag: (e) => {
    console.log('开始拖拽', e)
  },
  onDrag: ({ x, y }) => {
    console.log('拖拽距离:', x, y)
  },
  onEndDrag: () => {
    console.log('结束拖拽')
  }
})
</script>

操作方式:

  • 🖱️ 左键点击画布并拖拽
  • ⌨️ 点击画布后,使用 ↑↓←→ 方向键微调(每次 30px)

自动聚焦机制

插件会在鼠标事件触发时自动设置 tabindex(如不存在),并调用 focus(),确保后续的键盘事件能够正确响应。无需手动在 HTML 中添加 tabindex="0"。

自定义触发规则

import { useCanvasDrag } from 'use-canvas-drag'
import type { DragTrigger, DragButtonConfig } from 'use-canvas-drag'

// 快捷方式
mode: 'right'              // 右键拖拽(默认)
mode: 'left'               // 左键拖拽
mode: 'middle'             // 中键拖拽

// 修饰键组合
mode: 'Shift+left'         // Shift + 左键
mode: 'Ctrl+right'         // Ctrl + 右键
mode: 'Alt+middle'         // Alt + 中键

// 多个触发键
mode: ['left', 'shift+right']  // 左键 或 shift+右键

// 自定义对象
const customMouse: DragButtonConfig = {
  button: 0,               // 0=左键, 1=中键, 2=右键
  modifiers: {
    shift: true,
    ctrl: true
  }
}

const { handlers } = useCanvasDrag({
  container: '#canvas',
  mode: [customMouse, 'right']
})

边界限制

const { handlers } = useCanvasDrag({
  container: '#canvas',
  mode: 'right',
  // 方式1: 自动限制在内容范围内
  bounds: true,
  // 方式2: 指定精确边界
  bounds: {
    left: 0,
    right: 1000,
    top: 0,
    bottom: 800
  }
})

API

useCanvasDrag(options)

| 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | container | string \| HTMLElement \| () => HTMLElement | ✅ | 画布容器,支持选择器、元素或返回元素的函数 | | mode | DragTrigger \| DragTrigger[] | ❌ | 鼠标触发方式,默认 'right'。支持 'left' / 'right' / 'middle' / 'Shift+left' 等 | | arrowKeys | boolean | ❌ | 启用方向键微调(↑↓←→),默认 false | | bounds | boolean \| 'content' \| { top?: number; right?: number; bottom?: number; left?: number; } | ❌ | 可选边界限制,控制拖拽范围 | | enabled | boolean \| Ref<boolean> | ❌ | 是否启用,默认 true(支持响应式) | | onStartDrag | (e: MouseEvent) => void | ❌ | 开始拖拽时触发(仅鼠标模式) | | onDrag | (info: { x: number; y: number }) => void | ❌ | 拖拽过程中触发(鼠标和方向键模式均触发) | | onEndDrag | () => void | ❌ | 结束拖拽时触发(仅鼠标模式) |

返回值

| 属性 | 类型 | 说明 | |------|------|------| | isPanning | Ref<boolean> | 当前是否正在拖拽 | | handlers | { onMouseDown, onContextMenu, onKeyDown } | 需要绑定到容器元素 | | stopPan | () => void | 强制停止拖拽 |

DragTrigger

DragTrigger 支持以下形式:

  • 字符串:'left'、'right'、'middle'、'Shift+left'、'Ctrl+right' 等
  • 对象:DragButtonConfig
  • 数组:组合多个触发规则
// 快捷方式
mode: 'right'

// 修饰键组合
mode: 'Shift+left'
mode: ['Shift+left', 'Ctrl+right']

// 自定义鼠标规则
mode: {
  button: 0,
  modifiers: { ctrl: true }
}

CanvasDragOptions

interface CanvasDragOptions {
  container: string | HTMLElement | (() => HTMLElement | null)
  mode?: DragTrigger | DragTrigger[]
  arrowKeys?: boolean                    // 启用方向键微调
  enabled?: boolean | Ref<boolean>
  onStartDrag?: (e: MouseEvent) => void
  onDrag?: (detail: { x: number; y: number }) => void
  onEndDrag?: () => void
  bounds?: boolean | 'content' | {
    left?: number
    right?: number
    top?: number
    bottom?: number
  }
}

DragButtonConfig

interface DragButtonConfig {
  button?: number  // 0=左键, 1=中键, 2=右键
  modifiers?: {
    shift?: boolean
    ctrl?: boolean
    alt?: boolean
    meta?: boolean
  }
}

常见问题

Q: 右键菜单被阻止了吗?

A: 自动处理! 当 mode 配置包含纯右键(不带修饰键)时,插件会自动禁用浏览器右键菜单。若右键带有修饰键(如 shift+right),则不会全局禁用右键菜单。

Q: 光标样式需要手动设置吗?

A: 自动处理! 插件会根据 mode 配置自动切换光标:

  • 包含纯左键(不带修饰键)→ grab(默认)/ grabbing(拖拽中)
  • 仅右键、修饰键组合或其他 → 保持默认光标

例如 mode: 'left' 显示 grab,而 mode: 'shift+left' 保持默认光标(需按住 Shift 才触发拖拽)。

Q: 如何使用方向键微调?

A: 设置 arrowKeys: true 即可启用。方向键每次触发移动 30px,无需在 mode 中配置。

useCanvasDrag({
  mode: 'left',
  arrowKeys: true
})

Q: 鼠标模式和方向键模式的区别是什么?

| 特性 | 鼠标模式 | 方向键模式 | |------|---------|-----------| | 触发方式 | 按下鼠标按钮 | 按下方向键 | | 开始/结束回调 | ✅ onStartDrag / onEndDrag | ❌ 无 | | 拖拽回调 | ✅ onDrag | ✅ onDrag | | isPanning 状态 | ✅ 支持 | ❌ 不适用 |

Q: 支持多画布吗?

A: 支持,只需为每个画布创建一个 useCanvasDrag 实例即可。

更新日志

v0.5.6 (2026-05-18)

  • 🐛 修复修饰键大小写 bug - parseTrigger 中修饰键比较已改用小写,确保 'shift+left'、'Shift+Left'、'SHIFT+LEFT' 均能正确识别
  • ✨ 右键菜单智能禁用 - 只有纯右键(不带修饰键)才自动禁用浏览器右键菜单
  • ✨ 光标智能切换 - 只有纯左键(不带修饰键)才自动显示 grab / grabbing 光标,修饰键组合保持默认光标

v0.5.1 (2026-05-17)

  • 🔧 简化 mode 类型 - 移除 KeyboardTriggerConfig,鼠标触发器统一使用 DragButtonConfig
  • 🔧 独立方向键模式 - 新增 arrowKeys 选项,替代在 mode 中配置方向键
  • 🔧 优化解析逻辑 - 'Alt+left' 等组合键正确解析为鼠标触发器

v0.5.0 (2026-05-17)

  • ✨ 新增键盘方向键支持 - 支持 ArrowUp、ArrowDown、ArrowLeft、ArrowRight
  • ✨ 自动聚焦机制 - 鼠标事件触发时自动设置 tabindex 并 focus()
  • ✨ 双重触发校验 - shouldTrigger 同时验证修饰键和按键名,防止误触发
  • 🔧 优化边界限制 - 新增 bounds: true 和 bounds: 'content' 快捷方式

v0.4.1

  • 修复类型导出问题

License

MIT