interact.js
功能最全面的 JavaScript 交互库,唯一同时支持拖拽、缩放和旋转手势的主流解决方案。
功能全面的 JavaScript 交互库,唯一同时支持拖拽、缩放和旋转手势的主流解决方案,适用于画布、仪表盘、图片编辑器等复杂交互场景。
interact.js
功能最全面的 JavaScript 交互库,唯一同时支持拖拽、缩放和旋转手势的主流解决方案。
技术简介说明
interact.js 是一款功能全面的 JavaScript 交互库,由 Taye Ogunmade 创建,专注于拖拽(Drag)、缩放(Resize)和手势(Gesture)三大交互能力。它是主流库中唯一同时支持拖拽+缩放+旋转手势的解决方案,在画布/白板应用、仪表盘编辑器、图片编辑器等场景具有不可替代的地位。
与传统拖拽排序库不同,interact.js 本身不做任何排序。它提供原始的指针事件数据和位移增量(dx/dy),由开发者决定如何实现视觉反馈。这种"提供数据、不操作 UI"的设计哲学使其具有极高的灵活性,但也意味着列表排序等常见场景需要开发者自行实现逻辑。
interact.js 采用模块化架构,支持 tree-shaking 的 scoped packages,核心功能可按需导入。库体积约 40KB(gzip),支持现代浏览器 + IE9+,内建 TypeScript 类型定义。经过 10+ 年迭代,核心功能成熟稳定,npm 周下载量约 57 万次。
基本信息
- 官网: https://interactjs.io
- GitHub: https://github.com/taye/interact.js
- License: MIT
- 最新版本: v1.10.27(2024 年 3 月 28 日)
- 主要维护者: Taye Ogunmade(@taye)
- npm 包名:
interactjs - npm 周下载量: ~57 万
- GitHub Stars: ~12,900+
快速上手
安装
# npm
npm install interactjs --save
# 或 CDN
<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
# 模块化导入(推荐)
npm install @interactjs/interact基础配置
import interact from 'interactjs'
// 使元素可拖拽
interact('.draggable').draggable({
// 启用惯性
inertia: true,
// 限制在父元素内
modifiers: [
interact.modifiers.restrict({
restriction: 'parent',
}),
],
autoScroll: true,
listeners: {
move: dragMoveListener,
},
})
function dragMoveListener(event) {
const target = event.target
const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
target.style.transform = `translate(${x}px, ${y}px)`
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
}最小示例
<!DOCTYPE html>
<html>
<head>
<style>
#drag-me {
width: 200px;
height: 200px;
background: #4CAF50;
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: move;
touch-action: none; /* 防止拖拽时触发页面滚动 */
}
</style>
</head>
<body>
<div id="drag-me">拖拽我!</div>
<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
<script>
interact('#drag-me')
.draggable({
listeners: {
move(event) {
const target = event.target
const x = (parseFloat(target.dataset.x) || 0) + event.dx
const y = (parseFloat(target.dataset.y) || 0) + event.dy
target.style.transform = `translate(${x}px, ${y}px)`
target.dataset.x = x
target.dataset.y = y
}
}
})
// 使元素可缩放
.resizable({
edges: { top: true, left: true, bottom: true, right: true },
listeners: {
move(event) {
const target = event.target
target.style.width = `${event.rect.width}px`
target.style.height = `${event.rect.height}px`
}
}
})
</script>
</body>
</html>核心概念与架构
架构设计
interact.js 的架构围绕交互类型和修饰器两个核心维度构建:
┌─────────────────────────────────────────┐
│ interact() │
│ (核心函数,返回 Interactable) │
├─────────────────────────────────────────┤
│ │
│ 交互类型: │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │draggable│ │resizable │ │gesturable│ │
│ │(拖拽) │ │(缩放) │ │(手势) │ │
│ └────┬────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────▼───────────▼────────────▼─────┐ │
│ │ Pointer Events │ │
│ │ (统一鼠标/触摸/指针事件) │ │
│ └───────────────────────────────────┘ │
│ │
│ 修饰器 (Modifiers): │
│ ┌────────┐ ┌──────────┐ ┌───────────┐ │
│ │ snap │ │ restrict │ │aspectRatio│ │
│ │(吸附) │ │(限制范围) │ │(宽高比) │ │
│ └────────┘ └──────────┘ └───────────┘ │
│ │
│ 投放区 (Dropzone): │
│ ┌──────────┐ │
│ │ dropzone │ ← 定义可接受的投放目标 │
│ └──────────┘ │
│ │
│ 辅助: │
│ ┌──────────────┐ ┌─────────────┐ │
│ │ auto-scroll │ │ inertia │ │
│ │ (自动滚动) │ │ (惯性) │ │
│ └──────────────┘ └─────────────┘ │
└─────────────────────────────────────────┘
核心交互类型
| 交互类型 | 说明 | API 入口 |
|---|---|---|
| 拖拽 (Drag) | 元素在页面内自由移动 | .draggable() |
| 缩放 (Resize) | 调整元素尺寸 | .resizable() |
| 手势 (Gesture) | 多点触控的旋转和缩放 | .gesturable() |
| 投放区 (Dropzone) | 定义可接受的投放目标 | .dropzone() |
| 指针事件 | 统一的 tap/hold/doubletap 事件 | .on('tap'), .on('hold') |
事件模型
- 统一 API:鼠标和触摸事件使用相同接口处理
- 事件生命周期:
start→move→end - 多交互支持:多个元素可以同时被交互
- 事件对象关键属性:
event.dx / event.dy— 位移增量event.rect / event.deltaRect— 尺寸信息(缩放时)event.da— 角度变化(手势旋转时)event.scale— 缩放比例(手势缩放时)event.pageX / event.pageY— 指针位置
修饰器系统 (Modifiers)
修饰器是 interact.js 最强大的特性之一,用于动态约束交互行为:
| 修饰器 | 功能 | 说明 |
|---|---|---|
| snap | 吸附 | 吸附到网格、特定点或自定义函数 |
| restrict | 限制范围 | 限制在父元素、矩形区域或自定义边界内 |
| aspectRatio | 保持宽高比 | 缩放时保持比例,可在交互中切换开关 |
| endOnly | 仅结束时执行 | 仅在交互结束时执行修饰器 |
修饰器通过数组传入,按顺序依次执行,顺序可能影响最终结果:
interact('.element').draggable({
modifiers: [
interact.modifiers.snap({
targets: [interact.snappers.grid({ x: 20, y: 20 })],
}),
interact.modifiers.restrict({
restriction: 'parent',
}),
],
})核心特性
- 拖拽 + 缩放 + 旋转三合一:唯一同时支持三大交互的主流库,适合需要复合交互的应用
- 统一指针事件:鼠标、触摸、指针事件统一抽象,无需分别处理
- 强大的修饰器系统:snap(吸附)、restrict(限制)、aspectRatio(宽高比)可自由组合
- 惯性 (Inertia):拖拽结束后模拟物理惯性运动,提升交互体验
- 多点触控手势:支持双指旋转、缩放手势,触摸设备友好
- HTML 和 SVG 支持:包括嵌入在
<object>中的 SVG - 模块化架构:scoped packages 支持 tree-shaking,按需导入
- 自动滚动:拖拽到容器边缘时自动滚动(需手动启用)
- 投放区 (Dropzone):定义可接受的投放目标,支持拖拽放置
- 事件重放 (Reflow):可强制重新执行修饰器和监听器,用于动态更新
- CSS transform 支持:通过
data-x/data-y和 CSS transform 实现高性能平移
生态图
┌──────────────────┐
│ interactjs │ ← 核心库
│ v1.10.27 │
└────────┬─────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────────▼──────┐ ┌──────▼───────┐ ┌──────▼────────┐
│ @interactjs/ │ │ @interactjs/ │ │ @interactjs/ │
│ actions │ │ modifiers │ │ inertia │
│ (拖拽/缩放/ │ │ (约束系统) │ │ (物理惯性) │
│ 手势行为) │ │ │ │ │
└───────────────┘ └──────────────┘ └───────────────┘
模块化包:
┌──────────────────────────────────────────────┐
│ • @interactjs/interact (核心函数) │
│ • @interactjs/actions (拖拽/缩放/手势) │
│ • @interactjs/auto-start (指针移动触发) │
│ • @interactjs/modifiers (约束系统) │
│ • @interactjs/snappers (网格工具) │
│ • @interactjs/inertia (物理惯性) │
│ • @interactjs/reflow (事件重放) │
│ • @interactjs/auto-scroll (自动滚动) │
│ • @interactjs/dev-tools (开发工具) │
│ • @interactjs/types (TypeScript 类型) │
└──────────────────────────────────────────────┘
社区框架集成:
┌──────────────────────────────────────────────┐
│ • vue-interactjs (Vue 组件封装) │
│ • reactablejs (React 高阶组件封装) │
│ • 多种框架集成方案 │
└──────────────────────────────────────────────┘
适用场景
- 画布/白板应用:自由拖拽元素 + SVG 支持,核心能力完美匹配
- 仪表盘/编辑器布局:拖拽调整面板位置 + 缩放调整面板尺寸
- 图片/视频编辑器:拖拽定位 + 缩放尺寸 + 旋转角度
- 自由拖拽元素:将元素拖到页面任意位置(非排序语义)
- 自定义吸附/约束:修饰器系统极其灵活,支持网格吸附、边界限制、宽高比保持
- 需要复合交互的场景:同一元素同时需要拖拽+缩放+旋转
- 仪表盘拖拽布局:需要手动实现布局逻辑(如 grid 对齐)
开发与工程化
开发流程
# 克隆仓库
git clone https://github.com/taye/interact.js.git
cd interact.js
# 安装依赖
npm install
# 开发模式
npm run dev
# 构建
npm run build
# 测试
npm test项目结构
interact.js/
├── packages/
│ ├── core/ # 核心事件处理
│ ├── actions/ # 拖拽、缩放、手势行为
│ ├── modifiers/ # 约束系统(snap、restrict 等)
│ ├── snappers/ # 网格工具函数
│ ├── inertia/ # 物理惯性模拟
│ ├── auto-scroll/ # 自动滚动
│ ├── auto-start/ # 通过指针移动触发动作
│ ├── reflow/ # 修饰器/监听器重放
│ ├── dev-tools/ # 开发警告与构建优化
│ ├── types/ # TypeScript 类型定义
│ └── interact/ # 核心函数
├── test/ # 测试文件
└── dist/ # 构建输出
构建与部署
- 输出 ES2018 兼容代码
- 支持 ESM 和 UMD 格式
- 支持 tree-shaking(通过 scoped packages)
性能与安全
性能特点
- 模块化可 tree-shake:按需导入减少包体积
- 底层指针事件抽象:无多余 DOM 操作
- 不主动操作 DOM:开发者控制渲染方式,避免不必要的重排
- CSS transform 优先:推荐使用
transform而非top/left,保持 GPU 加速
性能优化建议
-
使用 CSS transform 而非 top/left:
// ✅ 推荐:使用 transform,性能更好 target.style.transform = `translate(${x}px, ${y}px)` // ❌ 避免:触发重排 // target.style.top = `${y}px` -
模块化导入减少体积:
import interact from '@interactjs/interact' import drag from '@interactjs/actions/drag' import modifiers from '@interactjs/modifiers' -
正确清理实例:防止内存泄漏
const interaction = interact('.element').draggable({...}) // 移除元素时必须调用 interaction.unset() -
auto-scroll 按需启用:默认关闭,仅需要时手动启用
安全注意事项
- interact.js 不涉及用户输入,无 XSS 风险
- 注意 DOM 元素移除后调用
unset()防止内存泄漏 - 拖拽坐标数据避免包含敏感信息
已知性能瓶颈
| 瓶颈 | 说明 |
|---|---|
| 复杂 SVG 拖拽卡顿 | 多层 SVG 元素拖拽时性能下降(Issue #770) |
| 内存泄漏风险 | DOM 元素移除后未正确 unset() 导致元素无法 GC(Issue #713) |
| 隐藏 dropzone 消耗 | 滚动容器中不可见的 dropzone 仍保持活跃(Issue #694) |
| CSS transform 缩放冲突 | 父元素有 scale() 时拖拽坐标计算不准确(Issue #137) |
技术对比
与交互/拖拽库对比
| 特性 | interact.js | Hammer.js | Dragula | SortableJS | dnd-kit |
|---|---|---|---|---|---|
| 拖拽 | ✅ | ❌ | ✅ | ✅ | ✅ |
| 缩放 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 旋转手势 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 触摸手势 | ✅ | ✅ (最专业) | ❌ | ❌ | ✅ |
| 列表排序 | 需手写 | ❌ | ✅ | ✅ (最佳) | ✅ |
| 修饰器/约束 | ✅ (强大) | ❌ | ❌ | 有限 | ❌ |
| 惯性 | ✅ | ❌ | ❌ | ❌ | ❌ |
| SVG 支持 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 维护状态 | ⚠️ 低活跃 | ❌ 不活跃 | ❌ 不活跃 | 维护模式 | ✅ 活跃 |
| React 友好度 | 一般 | 一般 | 一般 | 一般 | 优秀 |
| 包体积 | ~40KB | ~7KB | ~5KB | ~30KB | ~6KB |
选型建议
| 场景 | 推荐 |
|---|---|
| 拖拽 + 缩放 + 旋转一体 | interact.js(唯一选择) |
| 列表排序 | SortableJS 或 dnd-kit |
| 触摸手势识别 | Hammer.js(但已不活跃) |
| React 项目 | dnd-kit(更贴合 React 范式) |
| 画布/白板 | interact.js(SVG 支持 + 自由定位) |
| 简单拖拽 | interact.js 大材小用 |
最佳实践
生产环境最佳实践
-
移动端必须设置
touch-action: none:.draggable-element { touch-action: none; /* 防止拖拽时触发页面滚动 */ } -
使用 CSS transform 而非 top/left:
// ✅ 推荐:transform 性能更好,走 GPU 合成层 target.style.transform = `translate(${x}px, ${y}px)` // ❌ 避免:触发重排 // target.style.top = `${y}px` -
正确清理实例:
const interaction = interact('.element').draggable({...}) // 移除元素时必须调用,防止内存泄漏 interaction.unset() -
修饰器组合顺序很重要:
interact('.element').draggable({ modifiers: [ interact.modifiers.snap({ targets: [...] }), // 先吸附 interact.modifiers.restrict({ restriction: 'parent' }), // 再限制 ], }) -
模块化导入减少体积:
import interact from '@interactjs/interact' import drag from '@interactjs/actions/drag' import resize from '@interactjs/actions/resize' -
缩放时保持宽高比:
interact('.resize-me').resizable({ modifiers: [ interact.modifiers.aspectRatio({ ratio: 'preserve' }), ], }) -
拖拽手柄 + 缩放共存:
interact('.widget') .draggable({ handle: '.widget-header' }) .resizable({ edges: { bottom: true, right: true } })
常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 移动端拖拽同时滚动页面 | touchmove 事件未阻止 | 设置 touch-action: none |
| CSS scale 导致坐标偏移 | 父元素有 transform: scale() | 避免在交互元素上使用 CSS scale,或使用补偿计算 |
| iOS 偶发失效 | iOS 设备随机停止工作(Issue #549) | 确保正确初始化和清理实例 |
| snap + restrict 定位偏差 | 两者同时使用时可能冲突(Issue #554) | 调整修饰器顺序和参数 |
| 内存泄漏 | DOM 移除后未 unset() | 组件卸载时调用 interaction.unset() |
| drag handle + resize 配置复杂 | 手柄与缩放共存需要额外配置(Issue #402) | 仔细配置 handle 和 edges |
技术局限与边界
已知限制
| 限制 | 说明 |
|---|---|
| CSS transform 缩放冲突 | 父元素有 scale() 时拖拽坐标计算不准确 |
| 移动端 scroll/drag 冲突 | 拖拽时页面同时滚动,需手动处理 touch-action |
| iOS 偶发失效 | 在 iOS 设备上可能随机停止工作 |
| 无内建虚拟化 | 大量可拖拽元素时无自动优化 |
| 无列表排序能力 | 需手写排序逻辑,不如 SortableJS 开箱即用 |
| 无全局作用域隔离 | 多个实例共享全局状态 |
| 开发维护节奏放缓 | 自 2024 年 3 月以来无新版本发布(超 2 年) |
| React 友好度一般 | 需要手动管理实例生命周期,不如 dnd-kit 贴合 React 范式 |
| 学习曲线较陡 | 修饰器系统、事件模型需要理解多个概念 |
不适用场景
- 简单列表排序 → 推荐 SortableJS 或 dnd-kit(开箱即用)
- 纯 React 项目 → 推荐 dnd-kit(React 原生集成)
- 看板/卡片拖拽排序 → 推荐 dnd-kit 或 SortableJS
- 纯触摸手势识别 → Hammer.js 更专业(但已不活跃)
学习资源
- 官方文档:https://interactjs.io/docs
- 官方首页示例:https://interactjs.io(拖拽、缩放、手势代码示例)
- Modifiers 文档:https://interactjs.io/docs/modifiers/
- GitHub 仓库:https://github.com/taye/interact.js
- Mozilla Hacks 文章:interact.js for drag and drop, resizing and multi-touch gestures
- React 集成教程:dev.to - Make Drag and Drop with interact.js + React
- YouTube 视频:Drifting Ruby #075 - Drag and Drop with interact.js
- Gitter 社区:gitter.im/taye/interact.js
- 中文教程:掘金 - interact.js 入门教程
2026 年现状
维护状态评估
| 指标 | 状态 |
|---|---|
| 最新版本 | v1.10.27(2024 年 3 月 28 日) |
| 距上次发布 | 超过 2 年 |
| 维护节奏 | 2023-2024 年初密集发布了 10 个补丁版本,之后停滞 |
| npm 周下载量 | ~57 万/周(大量存量项目) |
| GitHub Stars | ~12,900+ |
| Twitter/X | @interactjs 自 2022 年 3 月后无更新 |
| 维护状态 | ⚠️ 稳定/低活跃 |
近期版本变更摘要
| 版本 | 主要变更 |
|---|---|
| v1.10.27 | 修复 skipLibCheck: false 时的 TypeScript 问题 |
| v1.10.26 | 改进构建流程,验证 ES2018 兼容性 |
| v1.10.25 | 输出改为 ES5 语法 |
| v1.10.24 | 生成 API 文档 |
| v1.10.23 | 修复 nullish coalescing 和 symbol-tree 构建问题 |
| v1.10.22 | 修复启用 inertia 时的手势错误 |
| v1.10.21 | 修复 drop 事件目标的回归问题 |
| v1.10.20 | 导入插件支持模块增强 |
| v1.10.18 | 移除 types 依赖、支持 aspectRatio 修饰器切换、修复 ESM 导出 |
发展趋势
- 稳定但低活跃:核心功能成熟,但新功能开发停滞
- 无明确 v2 路线图:社区未见重大架构升级计划
- 存量用户庞大:57 万周下载量说明广泛使用
- 新项目选型分化:React 项目推荐 dnd-kit;拖拽+缩放需求 interact.js 仍是最佳选择
- 被逐步替代:新项目推荐中逐渐被 dnd-kit、SortableJS 等替代
选型结论
interact.js 在 2026 年处于「稳定/低活跃」状态(超 2 年无新版本)。 当需要拖拽 + 缩放 + 旋转手势一体化时,它仍是唯一的主流选择。但如果是纯拖拽排序场景,推荐使用更专注的库(SortableJS、dnd-kit)。新项目选型需谨慎评估维护风险。