当前位置: 首页 > 文档资料 > D3.js 帮助文档 >

d3-drag

优质
小牛编辑
112浏览
2023-12-01

Drag-and-drop 是一种易学流行的交互手势:将指针指向目标对象,按下并且拖动它到一个新的位置,然后释放。D3 的 drag behavior 提供了方便灵活并且抽象的拖拽交互。例如可以使用 D3 的拖拽与 force-directed graph 进行交互:

Force Dragging IIIForce Dragging II

你也可以使用 d3-drag 来实现自定义的用户交互,比如滑块。但是拖拽交互不仅仅是用来改变元素的位置的。还有许多手势可以通过拖拽实现,比如套索一些算下,或者在 canvas 上画线:

Line Drawing

拖拽交互可以与其他的交互结合使用,比如 `d3-zoom

Drag & Zoom II

拖拽行为与 DOM 无关,因此你可以在 SVGHTML 甚至 Canvas 中使用它。并且你可以通过高级选择技术来扩展它比如 Voronoi 或者邻近搜索:

Circle Dragging IVCircle Dragging II

最重要的是,拖拽交互自动统一鼠标和触摸输入,并且屏蔽了不同浏览器的不同特性。当 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>

在浏览器中测试 d3-drag

API Reference

下表描述了拖拽行为如何解析原生事件:

Event(事件)Listening Element(监听元素)Drag Event(拖拽事件)Default Prevented?(阻止默认行为)
mousedown⁵selectionstartno¹
mousemove²window¹dragyes
mouseup²window¹endyes
dragstart²window-yes
selectstart²window-yes
click³window-yes
touchstartselectionstartno⁴
touchmoveselectiondragyes
touchendselectionendno⁴
touchcancelselectionendno⁴

所有消耗型事件的传播都是被 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.moused3.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(当前被拖拽)的东西。当接收到启动输入事件时被计算,比如mousedowntouchstart. 在拖拽启动时立即被计算。主体通过随后的 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加速.)

返回的主体应该是一个暴露 xy 属性的对象,以便在拖动手势期间保留主体和指针的相对位置。如果主体为 nullundefined 则不会有拖拽手势触发,然而其他的输入事件仍然可以触发,参考 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);
  }
}