Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Touch fixes. #50

Merged
merged 3 commits into from
Aug 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Or you can have the brush recenter when you click outside the current selection:

## Installing

If you use NPM, `npm install d3-brush`. Otherwise, download the [latest release](https://github.com/d3/d3-brush/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-brush.v1.min.js) or as part of [D3 4.0](https://github.com/d3/d3). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported:
If you use NPM, `npm install d3-brush`. Otherwise, download the [latest release](https://github.com/d3/d3-brush/releases/latest). You can load as a [standalone library](https://d3js.org/d3-brush.v1.min.js) or as part of [D3](https://github.com/d3/d3). ES modules, AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported:

```html
<script src="https://d3js.org/d3-color.v1.min.js"></script>
Expand All @@ -35,23 +35,21 @@ var brush = d3.brush();
</script>
```

[Try d3-brush in your browser.](https://tonicdev.com/npm/d3-brush)

## API Reference

<a href="#brush" name="brush">#</a> d3.<b>brush</b>() [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L131 "Source")
<a href="#brush" name="brush">#</a> d3.<b>brush</b>() [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

Creates a new two-dimensional brush.

<a href="#brushX" name="brushX">#</a> d3.<b>brushX</b>() [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L123 "Source")
<a href="#brushX" name="brushX">#</a> d3.<b>brushX</b>() [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

Creates a new one-dimensional brush along the *x*-dimension.

<a href="#brushY" name="brushY">#</a> d3.<b>brushY</b>() [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L127 "Source")
<a href="#brushY" name="brushY">#</a> d3.<b>brushY</b>() [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

Creates a new one-dimensional brush along the *y*-dimension.

<a href="#_brush" name="_brush">#</a> <i>brush</i>(<i>group</i>) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L142 "Source")
<a href="#_brush" name="_brush">#</a> <i>brush</i>(<i>group</i>) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

Applies the brush to the specified *group*, which must be a [selection](https://github.com/d3/d3-selection) of SVG [G elements](https://www.w3.org/TR/SVG/struct.html#Groups). This function is typically not invoked directly, and is instead invoked via [*selection*.call](https://github.com/d3/d3-selection#selection_call). For example, to render a brush:

Expand Down Expand Up @@ -86,11 +84,11 @@ The brush also creates the SVG elements necessary to display the brush selection

The overlay rect covers the brushable area defined by [*brush*.extent](#brush_extent). The selection rect covers the area defined by the current [brush selection](#brushSelection). The handle rects cover the edges and corners of the brush selection, allowing the corresponding value in the brush selection to be modified interactively. To modify the brush selection programmatically, use [*brush*.move](#brush_move).

<a href="#brush_move" name="brush_move">#</a> <i>brush</i>.<b>move</b>(<i>group</i>, <i>selection</i>) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L189 "Source")
<a href="#brush_move" name="brush_move">#</a> <i>brush</i>.<b>move</b>(<i>group</i>, <i>selection</i>) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

Sets the active *selection* of the brush on the specified *group*, which must be a [selection](https://github.com/d3/d3-selection) or a [transition](https://github.com/d3/d3-transition) of SVG [G elements](https://www.w3.org/TR/SVG/struct.html#Groups). The *selection* must be defined as an array of numbers, or null to clear the brush selection. For a [two-dimensional brush](#brush), it must be defined as [[*x0*, *y0*], [*x1*, *y1*]], where *x0* is the minimum *x*-value, *y0* is the minimum *y*-value, *x1* is the maximum *x*-value, and *y1* is the maximum *y*-value. For an [*x*-brush](#brushX), it must be defined as [*x0*, *x1*]; for a [*y*-brush](#brushY), it must be defined as [*y0*, *y1*]. The selection may also be specified as a function which returns such an array; if a function, it is invoked for each selected element, being passed the current datum `d` and index `i`, with the `this` context as the current DOM element. The returned array defines the brush selection for that element.

<a href="#brush_extent" name="brush_extent">#</a> <i>brush</i>.<b>extent</b>([<i>extent</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L521 "Source")
<a href="#brush_extent" name="brush_extent">#</a> <i>brush</i>.<b>extent</b>([<i>extent</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

If *extent* is specified, sets the brushable extent to the specified array of points [[*x0*, *y0*], [*x1*, *y1*]], where [*x0*, *y0*] is the top-left corner and [*x1*, *y1*] is the bottom-right corner, and returns this brush. The *extent* may also be specified as a function which returns such an array; if a function, it is invoked for each selected element, being passed the current datum `d` and index `i`, with the `this` context as the current DOM element. If *extent* is not specified, returns the current extent accessor, which defaults to:

Expand All @@ -105,7 +103,7 @@ This default implementation requires that the owner SVG element have defined [wi

The brush extent determines the size of the invisible overlay and also constrains the brush selection; the brush selection cannot go outside the brush extent.

<a href="#brush_filter" name="brush_filter">#</a> <i>brush</i>.<b>filter</b>([<i>filter</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L525 "Source")
<a href="#brush_filter" name="brush_filter">#</a> <i>brush</i>.<b>filter</b>([<i>filter</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

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

Expand All @@ -117,11 +115,23 @@ function filter() {

If the filter returns falsey, the initiating event is ignored and no brush gesture is 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.

<a href="#brush_handleSize" name="brush_handleSize">#</a> <i>brush</i>.<b>handleSize</b>([<i>size</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L529 "Source")
<a href="#brush_touchable" name="brush_touchable">#</a> <i>brush</i>.<b>touchable</b>([<i>touchable</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

If *touchable* is specified, sets the touch support detector to the specified function and returns the brush. If *touchable* is not specified, returns the current touch support detector, which defaults to:

```js
function touchable() {
return "ontouchstart" in this;
}
```

Touch event listeners are only registered if the detector returns truthy for the corresponding element when the brush is [applied](#_brush). The default detector works well for most browsers that are capable of touch input, but not all; Chrome’s mobile device emulator, for example, fails detection.

<a href="#brush_handleSize" name="brush_handleSize">#</a> <i>brush</i>.<b>handleSize</b>([<i>size</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

If *size* is specified, sets the size of the brush handles to the specified number and returns the brush. If *size* is not specified, returns the current handle size, which defaults to six. This method must be called before [applying the brush](#_brush) to a selection; changing the handle size does not affect brushes that were previously rendered.

<a href="#brush_on" name="brush_on">#</a> <i>brush</i>.<b>on</b>(<i>typenames</i>[, <i>listener</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L533 "Source")
<a href="#brush_on" name="brush_on">#</a> <i>brush</i>.<b>on</b>(<i>typenames</i>[, <i>listener</i>]) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

If *listener* is specified, sets the event *listener* for the specified *typenames* and returns the brush. 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](https://github.com/d3/d3-selection#selection_on) listeners: the current datum `d` and index `i`, with the `this` context as the current DOM element.

Expand All @@ -133,7 +143,7 @@ The *typenames* is a string containing one or more *typename* separated by white

See [*dispatch*.on](https://github.com/d3/d3-dispatch#dispatch_on) and [Brush Events](#brush-events) for more.

<a href="#brushSelection" name="brushSelection">#</a> d3.<b>brushSelection</b>(<i>node</i>) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js#L118 "Source")
<a href="#brushSelection" name="brushSelection">#</a> d3.<b>brushSelection</b>(<i>node</i>) [<>](https://github.com/d3/d3-brush/blob/master/src/brush.js "Source")

Returns the current brush selection for the specified *node*. Internally, an element’s brush state is stored as *element*.\_\_brush; however, you should use this method rather than accessing it directly. If the given *node* has no selection, returns null. Otherwise, the *selection* is defined as an array of numbers. For a [two-dimensional brush](#brush), it is [[*x0*, *y0*], [*x1*, *y1*]], where *x0* is the minimum *x*-value, *y0* is the minimum *y*-value, *x1* is the maximum *x*-value, and *y1* is the maximum *y*-value. For an [*x*-brush](#brushX), it is [*x0*, *x1*]; for a [*y*-brush](#brushY), it is [*y0*, *y1*].

Expand Down
15 changes: 11 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"name": "d3-brush",
"version": "1.0.6",
"version": "1.1.0-rc.1",
"publishConfig": {
"tag": "next"
},
"description": "Select a one- or two-dimensional region using the mouse or touch.",
"keywords": [
"d3",
Expand All @@ -22,6 +25,10 @@
"type": "git",
"url": "https://github.com/d3/d3-brush.git"
},
"files": [
"dist/**/*.js",
"src/**/*.js"
],
"scripts": {
"pretest": "rollup -c",
"test": "tape 'test/**/*-test.js' && eslint src",
Expand All @@ -36,9 +43,9 @@
"d3-transition": "1"
},
"devDependencies": {
"eslint": "5",
"rollup": "0.64",
"rollup-plugin-terser": "1",
"eslint": "6",
"rollup": "1",
"rollup-plugin-terser": "5",
"tape": "4"
}
}
34 changes: 25 additions & 9 deletions src/brush.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {dragDisable, dragEnable} from "d3-drag";
import {interpolate} from "d3-interpolate";
import {customEvent, event, mouse, select} from "d3-selection";
import {interrupt} from "d3-transition";
import constant from "./constant";
import BrushEvent from "./event";
import noevent, {nopropagation} from "./noevent";
import constant from "./constant.js";
import BrushEvent from "./event.js";
import noevent, {nopropagation} from "./noevent.js";

var MODE_DRAG = {name: "drag"},
MODE_SPACE = {name: "space"},
Expand Down Expand Up @@ -104,6 +104,10 @@ function defaultExtent() {
return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];
}

function defaultTouchable() {
return "ontouchstart" in this;
}

// Like d3.local, but with the name “__brush” rather than auto-generated.
function local(node) {
while (!node.__brush) if (!(node = node.parentNode)) return;
Expand Down Expand Up @@ -135,6 +139,7 @@ export default function() {
function brush(dim) {
var extent = defaultExtent,
filter = defaultFilter,
touchable = defaultTouchable,
listeners = dispatch(brush, "start", "brush", "end"),
handleSize = 6,
touchending;
Expand Down Expand Up @@ -182,8 +187,13 @@ function brush(dim) {
.each(redraw)
.attr("fill", "none")
.attr("pointer-events", "all")
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)")
.on("mousedown.brush touchstart.brush", started);
.on("mousedown.brush", started)
.filter(touchable)
.on("touchstart.brush", started)
.on("touchmove.brush", touchmoved)
.on("touchend.brush touchcancel.brush", touchended)
.style("touch-action", "none")
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
}

brush.move = function(group, selection) {
Expand Down Expand Up @@ -338,9 +348,8 @@ function brush(dim) {
.attr("cursor", cursors[type]);

if (event.touches) {
group
.on("touchmove.brush", moved, true)
.on("touchend.brush touchcancel.brush", ended, true);
emit.moved = moved;
emit.ended = ended;
} else {
var view = select(event.view)
.on("keydown.brush", keydowned, true)
Expand Down Expand Up @@ -429,7 +438,6 @@ function brush(dim) {
if (event.touches.length) return;
if (touchending) clearTimeout(touchending);
touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
group.on("touchmove.brush touchend.brush touchcancel.brush", null);
} else {
dragEnable(event.view, moving);
view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null);
Expand Down Expand Up @@ -511,6 +519,14 @@ function brush(dim) {
}
}

function touchmoved() {
emitter(this, arguments).moved();
}

function touchended() {
emitter(this, arguments).ended();
}

function initialize() {
var state = this.__brush || {selection: null};
state.extent = extent.apply(this, arguments);
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export {
brushX,
brushY,
brushSelection
} from "./brush";
} from "./brush.js";
Loading