hickeye / d3-drag

Drag and drop SVG, HTML or Canvas using mouse or touch input.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

d3-drag

Drag-and-drop is a popular and easy-to-learn pointing gesture: move the pointer to an object, press and hold to grab it, “drag” the object to a new location, and release to “drop”. D3’s drag behavior provides a convenient but flexible abstraction for enabling drag-and-drop interaction on selections. For example, you can use d3-drag to facilitate interaction with a force-directed graph, or a simulation of colliding circles:

Force Dragging IIIForce Dragging II

You can also use d3-drag to implement custom user interface elements, such as a slider. But the drag behavior isn’t just for moving elements around; there are a variety of ways to respond to a drag gesture. For example, you can use it to lasso elements in a scatterplot, or to paint lines on a canvas:

Line Drawing

The drag behavior is agnostic about the DOM, so you can use it with SVG, HTML or even Canvas! And you can extend it with advanced selection techniques, such as a Voronoi overlay or a closest-target search:

Circle Dragging IVCircle Dragging II

Best of all, the drag behavior automatically unifies mouse and touch input, and avoids browser idiosyncrasies. When Pointer Events are more widely available, the drag behavior will support those, too.

Installing

If you use NPM, npm install d3-drag. Otherwise, download the latest release. You can also load directly from d3js.org, either as a standalone library or as part of D3 4.0. AMD, CommonJS, and vanilla environments are supported. In vanilla, a d3_drag global is exported:

<script src="https://d3js.org/d3-dispatch.v0.4.min.js"></script>
<script src="https://d3js.org/d3-selection.v0.7.min.js"></script>
<script src="https://d3js.org/d3-drag.v0.1.min.js"></script>
<script>

var drag = d3_drag.drag();

</script>

Try d3-drag in your browser.

API Reference

This table describes how the drag behavior interprets native events:

Event Listening Element Drag Event Default Prevented?
mousedown⁵ selection start no¹
mousemove² window³ drag yes
mouseup² window³ end no¹
touchstart selection start no¹
touchmove selection drag yes
touchend selection end no¹
touchcancel selection end no¹
selectstart² window - yes
dragstart² window - yes
click⁴ window - yes

The propagation of all consumed events is immediately stopped. If you want to prevent some events from initiating a drag gesture, use drag.filter.

¹ Default cannot be prevented due to browser bugs; see #9.
² Only applies during an active drag gesture.
³ Necessary to capture events outside an iframe; see #9.
⁴ Only applies immediately after a non-empty drag gesture ends.
⁵ Ignored if within 500ms of a touch drag gesture ending; assumes click emulation.

# d3.drag([started])

Creates a new drag behavior. If started is specified, registers the specified function as a start event listener via drag.on, equivalent to:

var drag = d3.drag().on("start", started);

The returned behavior, drag, is an object and a function, and can be applied to a selection by calling it.

# drag(selection)

Applies this drag behavior to the specified selection. This function is typically not invoked directly, and is instead invoked via selection.call. For example, to instantiate a drag behavior and apply it to a selection:

d3.selectAll(".node").call(d3.drag(started));

Internally, the drag behavior uses selection.on to bind the necessary event listeners for dragging. The listeners use the name .drag, so you can subsequently unbind the drag behavior as follows:

selection.on(".drag", null);

Applying the drag behavior also sets the -webkit-tap-highlight-color style to transparent, disabling the tap highlight on iOS. If you want a different tap highlight color, remove or re-apply this style after applying the drag behavior.

# drag.container([container])

If container is specified, sets the container accessor to the specified object or function and returns the drag behavior. If container is not specified, returns the current container accessor, which defaults to:

function container() {
  return this.parentNode;
}

The container of a drag gesture determines the coordinate system of subsequent drag events, affecting event.x and event.y. The element returned by the container accessor is subsequently passed to d3.mouse or d3.touch, as appropriate, to determine the local coordinates of the pointer.

The default container accessor returns the parent node of the element in the originating selection (see drag) that received the initiating input event. This is often appropriate when dragging SVG or HTML elements, since those elements are typically positioned relative to a parent. For dragging graphical elements with a Canvas, however, you may want to redefine the container as the initiating element itself:

function container() {
  return this;
}

Alternatively, the container may be specified as the element directly, such as drag.container(canvas).

# drag.filter([filter])

If filter is specified, sets the filter to the specified function and returns the drag behavior. If filter is not specified, returns the current filter, which defaults to:

function filter() {
  return !d3.event.button;
}

If the filter returns falsey, the initiating event is ignored and no drag gestures are started. Thus, the filter determines which input events are ignored; the default filter ignores mousedown events on secondary buttons, since those buttons are typically intended for other purposes, such as the context menu.

# drag.subject([subject])

If subject is specified, sets the subject accessor to the specified object or function and returns the drag behavior. If subject is not specified, returns the current subject accessor, which defaults to:

function subject(d) {
  return d;
}

The subject of a drag gesture represents the thing being dragged. It is computed when an initiating input event is received, such as a mousedown or touchstart, immediately before the drag gesture starts. The subject is then exposed as event.subject on subsequent drag events for this gesture.

The default subject is the datum of the element in the originating selection (see drag) that received the initiating input event. When dragging circle elements in SVG, the default subject is the datum of the circle being dragged; with Canvas, the default subject is the canvas element’s datum (regardless of where on the canvas you click). In this case, a custom subject accessor would be more appropriate, such as one that picks the closest circle to the mouse within a given search radius:

function subject() {
  var i = 0,
      n = circles.length,
      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;
}

(If necessary, the above can be accelerated using quadtree.find.)

The returned subject is typically an object that exposes x and y properties, so that the relative position of the subject and the pointer can be preserved during the drag gesture; see drag.x and drag.y. If the subject is null or undefined, no drag gesture is started for this pointer; however, other starting touches may yet start drag gestures. See also drag.filter.

The subject of a drag gesture may not be changed after the gesture starts. The subject accessor is invoked with the same context and arguments as selection.on listeners: the current datum d and index i, with the this context as the current DOM element. During the evaluation of the subject accessor, d3.event is a beforestart drag event. Use event.sourceEvent to access the initiating input event and event.identifier to access the touch identifier. The event.x and event.y are relative to the container, and are computed using d3.mouse or d3.touch as appropriate.

# drag.x([x])

If x is specified, sets the x-position accessor to the specified function and returns the drag behavior. If x is not specified, returns the current x-accessor, which defaults to:

function x() {
  return d3.event.subject.x;
}

The x- and y-accessors determine the starting position of the subject, such that the relative position of the subject (event.x and event.y) and the pointer is maintained during the drag gesture; see Drag Events. The default accessors assume the subject exposes x and y properties representing its position.

# drag.y([y])

If y is specified, sets the y-position accessor to the specified function and returns the drag behavior. If y is not specified, returns the current y-accessor, which defaults to:

function y() {
  return d3.event.subject.y;
}

The x- and y-accessors determine the starting position of the subject, such that the relative position of the subject (event.x and event.y) and the pointer is maintained during the drag gesture; see Drag Events. The default accessors assume the subject exposes x and y properties representing its position.

# drag.on(typenames, [listener])

If listener is specified, sets the event listener for the specified typenames and returns the drag behavior. If an event listener was already registered for the same type and name, the existing listener is removed before the new listener is added. If listener is null, removes the current event listeners for the specified typenames, if any. If listener is not specified, returns the first currently-assigned listener matching the specified typenames, if any. When a specified event is dispatched, each listener will be invoked with the same context and arguments as selection.on listeners: the current datum d and index i, with the this context as the current DOM element.

The typenames is a string containing one or more typename separated by whitespace. Each typename is a type, optionally followed by a period (.) and a name, such as drag.foo and drag.bar; the name allows multiple listeners to be registered for the same type. The type must be one of the following:

  • start - after a new pointer becomes active (on mousedown or touchstart).
  • drag - after an active pointer moves (on mousemove or touchmove).
  • end - after an active pointer becomes inactive (on mouseup, touchend or touchcancel).

See dispatch.on for more.

Changes to registered listeners via drag.on during a drag gesture do not affect the current drag gesture. Instead, you must use event.on, which also allows you to register temporary event listeners for the current drag gesture. Separate events are dispatched for each active pointer during a drag gesture. For example, if simultaneously dragging multiple subjects with multiple fingers, a start event is dispatched for each finger, even if both fingers start touching simultaneously. See Drag Events for more.

By default, the drag behavior does not prevent default behaviors on mousedown and touchstart; this is necessary because of a Chrome bug that prevents the capture of mousemove events outside an iframe, and because it would prevent clicks after touchend. When Pointer Events are more widely available, the drag behavior may change to prevent default behaviors on pointerdown. The drag behavior also does not stop event propagation of these initiating events by default. When combining the drag behavior with other behaviors (such as zooming; see example), you may wish to stop propagation on the source event:

drag.on("start", function() {
  d3.event.sourceEvent.stopPropagation(); // Don’t notify other listeners.
});

While mousedown and touchstart’s default behaviors are allowed, the drag behavior registers several default named listeners to prevent other browser default behaviors:

Remove a listener if you desire the corresponding browser default behavior; for example, drag.on(".noclick", null) enables clicks after dragging.

Drag Events

When a drag event listener is invoked, d3.event is set to the current drag event. The event object exposes several fields:

  • type - the string “start”, “drag” or “end”; see drag.on.
  • subject - the drag subject, defined by drag.subject.
  • x - the x-coordinate of the subject; see drag.x and drag.container.
  • y - the y-coordinate of the subject; see drag.y and drag.container.
  • identifier - the string “mouse”, or a numeric touch identifier.
  • active - the number of currently active drag gestures (on start and end, not including this one).
  • sourceEvent - the underlying input event, such as mousemove or touchmove.

The event.active field is useful for detecting the first start event and the last end event in a sequence of concurrent drag gestures: it is zero when the first drag gesture starts, and zero when the last drag gesture ends.

The event object also exposes the event.on method.

# event.on(typenames, [listener])

Equivalent to drag.on, but only applies to the current drag gesture. Before the drag gesture starts, a copy of the current drag event listeners is made. This copy is bound to the current drag gesture and modified by event.on. This is useful for temporary listeners that only receive events for the current drag gesture. For example, this start event listener registers temporary drag and end event listeners as closures:

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);
  }
}

About

Drag and drop SVG, HTML or Canvas using mouse or touch input.

License:BSD 3-Clause "New" or "Revised" License


Languages

Language:JavaScript 100.0%