interact.js 是一个 JavaScript 模块,用于拖放、调整大小和多点触控手势,并具有现代浏览器(以及 IE8+)的惯性和捕捉功能。
背景
我最初将其作为我的 GSoC 2012 项目 的一部分,用于 Biographer 的网络可视化工具。该工具是一个 Web 应用程序,它渲染到 SVG 画布并使用 jQuery UI 进行拖放、选择和调整大小。由于 jQuery UI 对 SVG 的支持有限,因此必须使用大量解决方法。我需要使 Web 应用程序在智能手机和平板电脑上更易用,而这项工作中最大的一块是将 jQuery UI 替换为 interact.js,它
- 轻量级,
- 与 SVG 兼容良好,
- 处理多点触控输入,
- 将渲染/样式化元素的任务留给应用程序,以及
- 允许应用程序提供对象尺寸,而不是解析元素样式或获取 DOMRects。
interact.js 试图做的是在不同的浏览器和设备之间一致地呈现输入数据,并提供方便的方法来假装用户做了他们实际上没有做的事情(捕捉、惯性等)。
某些用户输入序列会导致 InteractEvent
被触发。如果您为事件类型添加事件侦听器,则该函数将获得一个 InteractEvent
对象,该对象提供指针坐标和速度,以及在手势事件中提供缩放比例、距离、角度等。interact.js 修改 DOM 的唯一时间是为光标设置样式;在拖动过程中使元素移动必须通过您自己的事件侦听器完成。这样,您就可以控制发生的一切。
滑块演示
这是一个使用 interact.js 创建滑块的示例。您可以在 CodePen 上查看和编辑此帖子中所有演示的完整 HTML、CSS 和 JS 代码。
查看 CodePen 上 Taye A (@taye) 的 interact.js 简单滑块。
JavaScript 概述
interact('.slider') // target the matches of that selector
.origin('self') // (0, 0) will be the element's top-left
.restrict({drag: 'self'}) // keep the drag within the element
.inertia(true) // start inertial movement if thrown
.draggable({ // make the element fire drag events
max: Infinity // allow drags on multiple elements
})
.on('dragmove', function (event) { // call this function on every move
var sliderWidth = interact.getElementRect(event.target.parentNode).width,
value = event.pageX / sliderWidth;
event.target.style.paddingLeft = (value * 100) + '%';
event.target.setAttribute('data-value', value.toFixed(2));
});
interact.maxInteractions(Infinity); // Allow multiple interactions
interact('.slider')
[文档] 创建一个Interactable
对象,该对象针对与'.slider'
CSS 选择器匹配的元素。HTML 或 SVG 元素对象也可以用作目标,但使用选择器可以让您对多个元素使用相同的设置。.origin('self')
[文档] 告诉 interact.js 修改报告的坐标,以便目标元素左上角的事件为(0,0)
。.restrict({drag: 'self'})
[文档] 使坐标保持在目标元素区域内。.inertia(true)
[文档] 允许用户“抛出”目标,以便指针释放后目标继续移动。- 在对象上调用
.draggable({max: Infinity})
[文档]- 允许在用户从与目标匹配的元素拖动时调用拖动侦听器,以及
- 允许同时拖动多个目标元素
.on('dragmove', function (event) {...})
[文档] 添加了dragmove
事件的侦听器。每当发生dragmove
事件时,将调用添加到目标 Interactable 的该事件类型的所有侦听器。此处的侦听器函数根据滑块宽度上的哪个点发生了拖动来计算 0 到 1 之间的值。此值用于定位手柄。interact.maxInteractions(Infinity)
[文档] 是启用任何目标上的多个交互所需的。默认值为1
,以保持向后兼容性。
interact.js 解决了许多浏览器实现差异。MouseEvents、TouchEvents 和 PointerEvents 将生成相同的拖动事件对象,因此此滑块在 iOS、Android、Firefox OS 和 Windows RT 以及桌面浏览器(包括 IE8 及更高版本)上都能正常工作。
彩虹像素画布演示
interact.js 不仅仅用于在页面上移动元素。在这里,我使用它在画布元素上绘制。
查看 CodePen 上 Taye A (@taye) 的 interact.js 像素彩虹画布。
JavaScript 概述
var pixelSize = 16;
interact('.rainbow-pixel-canvas')
.snap({
// snap to the corners of a grid
mode: 'grid',
// specify the grid dimensions
grid: { x: pixelSize, y: pixelSize }
})
.origin('self')
.draggable({
max: Infinity,
maxPerElement: Infinity
})
// draw colored squares on move
.on('dragmove', function (event) {
var context = event.target.getContext('2d'),
// calculate the angle of the drag direction
dragAngle = 180 * Math.atan2(event.dx, event.dy) / Math.PI;
// set color based on drag angle and speed
context.fillStyle = 'hsl(' + dragAngle + ', 86%, '
+ (30 + Math.min(event.speed / 1000, 1) * 50) + '%)';
// draw squares
context.fillRect(event.pageX - pixelSize / 2, event.pageY - pixelSize / 2,
pixelSize, pixelSize);
})
// clear the canvas on doubletap
.on('doubletap', function (event) {
var context = event.target.getContext('2d');
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
});
function resizeCanvases () {
[].forEach.call(document.querySelectorAll('.rainbow-pixel-canvas'), function (canvas) {
canvas.width = document.body.clientWidth;
canvas.height = window.innerHeight * 0.7;
});
}
// interact.js can also add DOM event listeners
interact(document).on('DOMContentLoaded', resizeCanvases);
interact(window).on('resize', resizeCanvases);
interact.maxInteractions(Infinity);
捕捉 用于修改指针坐标,使它们始终与网格对齐。
.snap({
// snap to the corners of a grid
mode: 'grid',
// specify the grid dimensions
grid: { x: pixelSize, y: pixelSize }
})
与之前的演示类似,启用了多个拖动,但需要更改额外的选项 maxPerElement
,以允许在同一个元素上进行多个拖动。
.draggable({
max: Infinity,
maxPerElement: Infinity
})
移动角度使用 Math.atan2(event.dx, event.dy)
计算,并用于设置油漆颜色的色调。event.speed
用于调整亮度。
interact.js 具有轻触和双击事件,这些事件等效于单击和双击,但没有移动设备上的延迟。此外,与常规单击事件不同,如果鼠标在释放之前移动,则不会触发轻触。(我正在努力添加更多像这样的事件)。
// clear the canvas on doubletap
.on('doubletap', function (event) {
...
它还可以侦听常规 DOM 事件。在上面的演示中,它用于侦听窗口大小调整和文档 DOMContentLoaded。
interact(document).on('DOMContentLoaded', resizeCanvases);
interact(window).on('resize', resizeCanvases);
类似于 jQuery,它也可以用于委托事件。例如
interact('input', { context: document.body })
.on('keypress', function (event) {
console.log(event.key);
});
提供元素尺寸
为了获取元素尺寸,interact.js 通常使用
Element#getBoundingClientRect()
用于 SVGElements,以及Element#getClientRects()[0]
用于 HTMLElements(因为它包含元素的边框)
并添加页面滚动。这在检查要对元素执行的操作、检查放置、计算 'self'
原点以及其他一些地方时完成。如果您的应用程序保留了正在交互的元素的尺寸,那么使用应用程序的数据而不是获取 DOMRect 更有意义。为了允许这样做,Interactable
具有 rectChecker()
[文档] 方法来更改获取元素尺寸的方式。该方法接受一个函数作为参数。当 interact.js 需要元素的尺寸时,该元素将传递给该函数,并使用返回值。
图形编辑器演示
下面的“SVG 编辑器”有一个 Rectangle
类,用于表示 DOM 中的 <rect class="edit-rectangle"/>
元素。每个矩形对象都有尺寸、用户看到的元素和一个绘制方法。
查看 CodePen 上 Taye A (@taye) 的 Interactable#rectChecker 演示。
JavaScript 概述
var svgCanvas = document.querySelector('svg'),
svgNS = 'http://www.w3.org/2000/svg',
rectangles = [];
function Rectangle (x, y, w, h, svgCanvas) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.stroke = 5;
this.el = document.createElementNS(svgNS, 'rect');
this.el.setAttribute('data-index', rectangles.length);
this.el.setAttribute('class', 'edit-rectangle');
rectangles.push(this);
this.draw();
svgCanvas.appendChild(this.el);
}
Rectangle.prototype.draw = function () {
this.el.setAttribute('x', this.x + this.stroke / 2);
this.el.setAttribute('y', this.y + this.stroke / 2);
this.el.setAttribute('width' , this.w - this.stroke);
this.el.setAttribute('height', this.h - this.stroke);
this.el.setAttribute('stroke-width', this.stroke);
}
interact('.edit-rectangle')
// change how interact gets the
// dimensions of '.edit-rectangle' elements
.rectChecker(function (element) {
// find the Rectangle object that the element belongs to
var rectangle = rectangles[element.getAttribute('data-index')];
// return a suitable object for interact.js
return {
left : rectangle.x,
top : rectangle.y,
right : rectangle.x + rectangle.w,
bottom: rectangle.y + rectangle.h
};
})
每当 interact.js 需要获取其中一个 '.edit-rectangle'
元素的尺寸时,它都会调用指定的 rectChecker
函数。该函数使用元素参数找到 Rectangle 对象,然后创建一个包含 left
、right
、top
和 bottom
属性的适当对象并返回它。
此对象用于在设置 限制元素矩形 选项时进行限制。在之前的滑块演示中,限制只使用指针坐标。在这里,限制将尝试阻止元素被拖出指定的区域。
.inertia({
// don't jump to the resume location
// https://github.com/taye/interact.js/issues/13
zeroResumeDelta: true
})
.restrict({
// restrict to a parent element that matches this CSS selector
drag: 'svg',
// only restrict before ending the drag
endOnly: true,
// consider the element's dimensions when restricting
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
})
矩形可拖动和可调整大小。
.draggable({
max: Infinity,
onmove: function (event) {
var rectangle = rectangles[event.target.getAttribute('data-index')];
rectangle.x += event.dx;
rectangle.y += event.dy;
rectangle.draw();
}
})
.resizable({
max: Infinity,
onmove: function (event) {
var rectangle = rectangles[event.target.getAttribute('data-index')];
rectangle.w = Math.max(rectangle.w + event.dx, 10);
rectangle.h = Math.max(rectangle.h + event.dy, 10);
rectangle.draw();
}
});
interact.maxInteractions(Infinity);
开发和贡献
我希望本文能很好地概述如何使用 interact.js 以及我认为它将对哪些类型的应用程序有用。如果不是,在 项目主页 上还有更多演示,您也可以在 Twitter 或 Github 上提出问题或发布问题。我真的很想制作一套全面的示例和文档,但我一直太忙于修复和改进。(我也太懒了 :-P)。
自 1.0.0 版本发布以来,用户评论和贡献带来了大量的错误修复和许多新功能,包括
因此请使用它、分享它、破坏它,并帮助使其变得更好!
关于 Taye Adeyemi
Web 开发人员,对用户交互和界面设计感兴趣。interact.js 的作者。目前在都柏林三一学院学习计算机科学,并通过参与开源项目、练习卡波耶拉以及学习意大利语和德语来保持头脑清醒。
关于 Robert Nyman [荣誉编辑]
Mozilla Hacks 的技术布道者和编辑。进行关于 HTML5、JavaScript 和开放网络的演讲和博客。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年以来一直从事网络前端开发工作 - 在瑞典和纽约市。他还经常在 http://robertnyman.com 上发表博客,喜欢旅行和结识新朋友。
2 条评论