在前端项目中,拖拽、缩放、多点触控、吸附、惯性等交互常常需要复杂的逻辑处理。而 Interact.js 作为一个仅约 10KB(gzipped)的小型库,却能以非常优雅、灵活的方式解决这些需求,既可以用于普通 DOM,也能用于 SVG、Canvas,甚至适配移动设备的手势交互。
这篇文章将带你全面认识 Interact.js 的能力与使用方式,帮助你在项目中优雅处理复杂的交互场景。
一、为什么选择 Interact.js?
相比于传统的拖拽方式(如 HTML5 Drag API 或自己监听 mousedown/move/up),Interact.js 带来了更多优势:
1. 轻量、独立、不依赖其他库
- gzipped 体积仅约 10KB
- 不依赖 jQuery / Lodash / Hammer.js
- 可按需启用不同 Action
2. 支持多点触控与移动端体验
- 直接支持 touch 事件
- 多指缩放/旋转(Gesturable)
3. 功能丰富,满足真实场景
- 惯性拖拽、吸附(snapping)
- 边界限制(restrict)
- Dropzone 放置区
- 自动滚动、长按触发
- 手动启动拖拽行为(manualStart)
4. 与现代浏览器和 IE9+ 兼容
- 支持 Chrome / Firefox / Safari / Opera / IE9+
- 支持桌面浏览器与移动端浏览器
5. 不改变 DOM(除光标样式,可禁用)
你完全掌握最终的 DOM 更新方式,它只负责事件与计算逻辑。
二、安装与引入方式
npm 安装
npm install interactjs
CDN 引入
<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
仅使用 TypeScript 类型
npm install --save-dev @interactjs/types
文档地址:
https://interactjs.io/docs
三、核心概念:Action(操作)
Interact.js 的交互操作分为三类 Action:
| Action 名称 | 描述 |
|---|---|
| Draggable | 拖拽移动元素 |
| Resizable | 调整元素大小 |
| Gesturable | 多点触控(缩放、旋转) |
使用方式也非常一致。
1. Draggable 示例
<style>
.draggable {
width: 200px;
height: 100px;
background: #58a;
user-select: none;
touch-action: none;
}
</style>
<div class="draggable">拖我试试</div>
<script>
const position = { x: 0, y: 0 }
// 构建可拖拽的元素
interact('.draggable').draggable({
onstart (e) {
console.log('开始拖拽', e.target)
},
onmove (e) {
// 更新位置
position.x += e.dx
position.y += e.dy
// 应用位移效果
e.target.style.transform = `translate(${position.x}px, ${position.y}px)`
},
onend (e) {
console.log('结束拖拽')
}
})
</script>
三种事件绑定方式任选其一(onXXX、listeners、.on),语法完全等价。
四、Dropzone:放置区
拖拽配合放置区可以实现拖放功能:
interact('.dropzone').dropzone({
accept: '.draggable',
overlap: 1,
ondragenter: e => {
e.target.classList.add('active')
},
ondragleave: e => {
e.target.classList.remove('active')
},
ondrop: e => {
console.log('已放置', e.relatedTarget)
}
})
使用 Draggable + Dropzone 就能实现拖放排序、拖拽上传等场景。
五、Modifier:吸附、限制等扩展能力
Modifiers 是 Interact.js 的灵魂,它允许你在事件触发前修改坐标,从而实现复杂交互!
1. 限制拖拽范围 restrict
interact('.box').draggable({
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent'
})
]
})
效果:元素永远不会超出父容器边界。
2. 吸附效果 snapping
吸附到最近的网格点:
interact('.box').draggable({
modifiers: [
interact.modifiers.snap({
targets: [interact.snappers.grid({ x: 40, y: 40 })],
range: 20
})
]
})
还能通过函数实现动态吸附点(例如吸附到某个动态位置):
function dynamicSnap (x, y) {
return { x: window.innerWidth / 2, y: 300 }
}
interact('.box').draggable({
modifiers: [
interact.modifiers.snap({
targets: [dynamicSnap]
})
]
})
六、Gesturable:多点触控(移动端)
interact('.gesture').gesturable({
onmove (e) {
const scale = e.scale
const angle = e.angle
// 手势事件中可操作元素,例如缩放
e.target.style.transform = `scale(${scale}) rotate(${angle}deg)`
}
})
适合移动端图片缩放、旋转等操作。
七、Pointer 原子事件:tap、doubletap、hold
如果你只需要基础事件,也可以直接监听:
interact('.btn').on('hold', e => {
console.log('长按触发', e.target)
})
支持事件类型:
- down
- move
- up
- tap
- doubletap
- hold
八、Canvas 示例:用拖拽“绘画”
官方示例中有一段非常有意思:拖拽绘制彩色方块。
var pixelSize = 16;
interact('.rainbow-pixel-canvas')
.origin('self')
.draggable({
modifiers: [
interact.modifiers.snap({
targets: [
interact.snappers.grid({ x: pixelSize, y: pixelSize }),
],
})
],
listeners: {
move: function (event) {
var context = event.target.getContext('2d')
var dragAngle =
180 * Math.atan2(event.dx, event.dy) / Math.PI
context.fillStyle = 'hsl(' + dragAngle + ', 86%, '
+ (30 + Math.min(event.speed / 1000, 1) * 50)
+ '%)'
context.fillRect(
event.pageX - pixelSize / 2,
event.pageY - pixelSize / 2,
pixelSize,
pixelSize
)
}
}
})
拖拽方向与速度决定颜色变化,这展示了 Interact.js 在 Canvas 场景中的灵活性。
九、适用场景与使用建议
适用场景
- 拖拽组件(拖拽排序、自由拖拽)
- 可缩放元素(图片编辑器、面板拉伸)
- 多指缩放旋转(移动端编辑器)
- 使用 Canvas 的绘图类交互
- 网格吸附布局器
- 自定义拖拽动画和效果
- 拖放上传 / 拖放分区
项目实践建议
- 用 Modifier 限制边界,可避免频繁坐标判断
- 当需要高性能场景(如 Canvas)时,把 DOM 操作减少到最小
- 结合 transform 使用,可获得最佳性能
- 手势(Gesturable)需要注意移动端触屏冲突(touch-action)
十、总结
Interact.js 是一个小巧但高度灵活的 JS 交互库,它只负责事件与计算,不替你做 DOM 操作,因此它能够应用于各种 UI、Canvas、SVG 甚至自定义渲染环境。
它非常适合:
- 想快速实现拖拽、缩放、手势功能的开发者
- 想要定制复杂吸附、限制、弹性效果的项目
- 需要移动端多点触控支持的应用
- 希望保持代码灵活、结果可控的前端工程师
一句话总结:
Interact.js 提供了“交互逻辑”,而你决定“如何渲染”。灵活到你可以用它做一个拖拽组件,也可以做一个小游戏。
GitHub 仓库:
https://github.com/taye/interact.js
文章评论