d3-drag
Drag-and-drop 是一种易学流行的交互手势:将指针指向目标对象,按下并且拖动它到一个新的位置,然后释放。D3
的 drag behavior 提供了方便灵活并且抽象的拖拽交互。例如可以使用 D3
的拖拽与 force-directed graph 进行交互:
你也可以使用 d3-drag
来实现自定义的用户交互,比如滑块。但是拖拽交互不仅仅是用来改变元素的位置的。还有许多手势可以通过拖拽实现,比如套索一些算下,或者在 canvas
上画线:
拖拽交互可以与其他的交互结合使用,比如 `d3-zoom:
拖拽行为与 DOM
无关,因此你可以在 SVG
、HTML
甚至 Canvas
中使用它。并且你可以通过高级选择技术来扩展它比如 Voronoi
或者邻近搜索:
最重要的是,拖拽交互自动统一鼠标和触摸输入,并且屏蔽了不同浏览器的不同特性。当 Pointer Events 可用时,拖拽事件也同样可用。
Installing
NPM: npm install d3-drag
. 还可以下载 latest release,可以作为单独的 standalone library,也可以作为 D3 v4 的一部分直接引入. 支持 AMD
, CommonJS
以及 vanilla
环境。如果使用 vanilla
则会暴露全局 d3
变量:
<script src="https://d3js.org/d3-dispatch.v1.min.js"></script>
<script src="https://d3js.org/d3-selection.v1.min.js"></script>
<script src="https://d3js.org/d3-drag.v1.min.js"></script>
<script>
var drag = d3.drag();
</script>
API Reference
下表描述了拖拽行为如何解析原生事件:
Event(事件) | Listening Element(监听元素) | Drag Event(拖拽事件) | Default Prevented?(阻止默认行为) |
---|---|---|---|
mousedown⁵ | selection | start | no¹ |
mousemove² | window¹ | drag | yes |
mouseup² | window¹ | end | yes |
dragstart² | window | - | yes |
selectstart² | window | - | yes |
click³ | window | - | yes |
touchstart | selection | start | no⁴ |
touchmove | selection | drag | yes |
touchend | selection | end | no⁴ |
touchcancel | selection | end | no⁴ |
所有消耗型事件的传播都是被 immediately stopped(立即停止) 的,如果你想阻止某些事件触发拖拽手势,请使用 drag.filter.
¹ 捕获 iframe
之外的事件是必须的; 参考 "https://github.com/d3/d3-drag/issues/9" target="_blank" rel="noopener noreferrer">"https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html" target="_blank" rel="noopener noreferrer">click emulation ; 参考 "https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html" target="_blank" rel="noopener noreferrer">click emulation.
"https://github.com/d3/d3-drag/blob/master/src/drag.js" title="Source" target="_blank" rel="noopener noreferrer"><源码>
创建一个新的拖拽行为并返回自身。drag 既是一个对象,也是一个函数,通常通过 selection.call 被应用在选中的元素上。
"https://github.com/d3/d3-drag/blob/master/src/drag.js" title="Source" target="_blank" rel="noopener noreferrer"><源码>
将拖拽应用到指定的selection。通常不适用这个方法应用拖拽,而是通过 selection.call。例如,将拖拽实例应用到一个选择集上:
d3.selectAll(".node").call(d3.drag().on("start", started));
在内部拖拽行为使用 selection.on将拖拽必需的事件绑定到元素上,事件名称都带有 .drag
,因此可以使用这个特殊的事件名来解绑拖拽事件:
selection.on(".drag", null);
应用拖拽行为时会将 -webkit-tap-highlight-color 样式设置为透明,禁止在 IOS
上的标签高亮。如果你想要不一样的样式,请在应用完拖拽行为之后移除或者重新设置这个属性。
"https://github.com/d3/d3-drag/blob/master/src/drag.js" title="Source" target="_blank" rel="noopener noreferrer"><源码>
如果指定了 container,则将拖拽行为的容器访问器设置为指定的对象或方法。如果没有指定 container ,则返回当前的容器访问器,默认为:
function container() {
return this.parentNode;
}
拖拽手势的 container 定义了随后 drag events 的坐标系统。影响 event.x 和 event.y。容器访问器返回的元素随后被传递给 d3.mouse 或 d3.touch,因此需要的时候要定义好容器访问器。
默认的容器访问器返回接收到初始事件的元素的父节点(参考 drag),在拖动 SVG
或者 HTML
元素时,通常是合理的,因为这些元素通常通过父元素定位。但是拖动 Canvas
中的元素时,你可能需要将容器访问器设置为 Canvas
自身:
function container() {
return this;
}
此外,设置容器访问器时,还可以直接将元素设置为参数: drag.container(canvas)
.
"https://github.com/d3/d3-drag/blob/master/src/drag.js" title="Source" target="_blank" rel="noopener noreferrer"><源码>
如果指定了 filter,则将 filter 设置为拖拽行为的过滤器。如果没有指定 filter 则返回当前的过滤器,默认为:
function filter() {
return !d3.event.button;
}
如果过滤器返回假,则初始事件会被忽略并且不会启动拖拽手势。也就是说过滤器可以定义哪些事件被忽略,默认的过滤器会忽略辅助按钮上的鼠标按下事件,因为这些按钮通常用作其他的作用,比如上下文菜单。
"https://github.com/d3/d3-drag/blob/master/src/drag.js" title="Source" target="_blank" rel="noopener noreferrer"><源码>
如果指定了 touchable,则设置触摸支持检测器为执行的函数。如果没有指定,则返回当前的触摸支持检测方法,默认为:
function touchable() {
return "ontouchstart" in this;
}
触摸事件监听器仅仅在触摸支持检测器返回真的时候才会被注册。默认的检测器在绝大多数浏览器中都能正常工作,但不是全部。例如Chrome的模拟移动仿真不会正常工作。
"https://github.com/d3/d3-drag/blob/master/src/drag.js" title="Source" target="_blank" rel="noopener noreferrer"><源码>
subject 此文译为 主体
, 如果 subject 指定,则为拖拽行为指定主体访问器。如果没有指定,则返回当前的主体访问器。默认为:
function subject(d) {
return d == null ? {x: d3.event.x, y: d3.event.y} : d;
}
主体代表的是 the thing being dragged(当前被拖拽)的东西。当接收到启动输入事件时被计算,比如mousedown
或touchstart
. 在拖拽启动时立即被计算。主体通过随后的 drag events 中的event.subject 来暴露。
默认情况下,主体为接收原始事件元素上的 datum. 如果 datum
没有定义则主体为输入事件的坐标(参考上述默认主体访问器)。当在 SVG
中拖拽时。默认的主体为当前元素绑定的数据,但在 Canvas
中默认的主体为 Canvas
本身绑定的数据(不关心拖拽事件触发的位置),此时自定义的主体访问器就显得有用了,比如可以将自定义主体访问器设置为鼠标事件坐标周围一定 半径
范围内的圆:
function subject() {
var n = circles.length,
i,
dx,
dy,
d2,
s2 = radius * radius,
circle,
subject;
for (i = 0; i < n; ++i) {
circle = circles[i];
dx = d3.event.x - circle.x;
dy = d3.event.y - circle.y;
d2 = dx * dx + dy * dy;
if (d2 < s2) subject = circle, s2 = d2;
}
return subject;
}
(如果需要的话可以使用 quadtree.find加速.)
返回的主体应该是一个暴露 x
和 y
属性的对象,以便在拖动手势期间保留主体和指针的相对位置。如果主体为 null
或 undefined
则不会有拖拽手势触发,然而其他的输入事件仍然可以触发,参考 Drag Events
当 drag event listener 被调用时, d3.event会被设置为当前的拖拽事件,event 对象暴露以下属性:
target
- 相关联的drag behavior.type
- 字符串 “start”, “drag” 或 “end”; 参考 drag.on.subject
- 通过 drag.subject定义的subject.x
- subject 的 x-坐标; 参考 drag.container.y
- subject 的 y-坐标; 参考 drag.container.dx
- 与上一次拖拽相比 x-坐标 的变化.dy
- 与上一次拖拽相比 y-坐标 的变化.identifier
- 字符串 “mouse”, 或者表示 touch identifier的数字.active
- 当前活动的拖拽手势的数量(在start和end, 不包含这个).sourceEvent
- 底层原始事件比如 mousemove 或 touchmove.
event.active 属性对判断并发的拖拽手势序列中的 start
事件和 end
事件: 在拖拽手势开始时为0,在拖拽结束最后一个手势事件时为0。
event 对象也暴露了 event.on 方法.
"https://github.com/d3/d3-drag/blob/master/src/event.js" title="Source" target="_blank" rel="noopener noreferrer"><源码>
与 drag.on 等价, 但是仅仅应用在当前的拖拽手势。在拖拽手势开始时会创建一个当前拖拽 event listeners 的 copy . 这个副本会被绑定到当前拖拽手势并且可以被 event.on 修改. 这对于仅接收当前手势的临时监听器很有用。例如下面事件监听器将临时拖拽事件以及结束事件注册为闭包:
function started() {
var circle = d3.select(this).classed("dragging", true);
d3.event.on("drag", dragged).on("end", ended);
function dragged(d) {
circle.raise().attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function ended() {
circle.classed("dragging", false);
}
}