diff --git a/README.md b/README.md index 22a5fe5..490ba86 100644 --- a/README.md +++ b/README.md @@ -169,3 +169,25 @@ When a [brush event listener](#brush_on) is invoked, it receives the current bru * `type` - the string “start”, “brush” or “end”; see [*brush*.on](#brush_on). * `selection` - the current [brush selection](#brushSelection). * `sourceEvent` - the underlying input event, such as mousemove or touchmove. + +The *event* object also exposes the [*event*.on](#event_on) method. + +# event.on(typenames, [listener]) · [Source](https://github.com/d3/d3-brush/blob/master/src/event.js) + +Equivalent to [*brush*.on](#brush_on), but only applies to the current brush gesture. Before the brush gesture starts, a [copy](https://github.com/d3/d3-dispatch#dispatch_copy) of the current brush [event listeners](#brush_on) is made. This copy is bound to the current brush gesture and modified by *event*.on. This is useful for temporary listeners that only receive events for the current brush gesture. For example, this start event listener registers temporary brush and end event listeners as closures: + +```js +function started(event, d) { + d3.select(this).classed("brushing", true); + + event.on("brush", brushed).on("end", ended); + + function brushed(event, d) { + console.log("brushing", d, "with", event.selection); + } + + function ended(event, d) { + console.log("brush on", d, "ended at", event.selection); + } +} +``` diff --git a/src/brush.js b/src/brush.js index 0707ac0..f7a8735 100644 --- a/src/brush.js +++ b/src/brush.js @@ -12,6 +12,8 @@ var MODE_DRAG = {name: "drag"}, MODE_HANDLE = {name: "handle"}, MODE_CENTER = {name: "center"}; +const {abs, max, min} = Math; + function number1(e) { return [+e[0], +e[1]]; } @@ -296,7 +298,7 @@ function brush(dim) { Emitter.prototype = { beforestart: function() { - if (++this.active === 1) this.state.emitter = this, this.starting = true; + if (++this.active === 1) this.state.emitter = this, this.starting = true, this.dispatch = listeners.copy(); return this; }, start: function() { @@ -313,8 +315,8 @@ function brush(dim) { return this; }, emit: function(type) { - var dispatch = listeners.copy(), - d = this.that.__data__; + var dispatch = this.dispatch, + d = select(this.that).datum(); dispatch.call( type, this.that, @@ -351,16 +353,26 @@ function brush(dim) { shifting = signX && signY && keys && event.shiftKey, lockX, lockY, - point0 = pointer(event.touches ? event.touches[0] : event, that), - point = point0, + points = Array.from(event.touches || [event], t => { + const i = t.identifier; + t = pointer(t, that); + t.point0 = t.slice(); + t.identifier = i; + return t; + }), emit = emitter(that, arguments, true).beforestart(); if (type === "overlay") { if (selection) moving = true; - state.selection = selection = [ - [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]], - [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0] - ]; + const pts = [points[0], points[1] || points[0]]; + state.selection = selection = [[ + w0 = dim === Y ? W : min(pts[0][0], pts[1][0]), + n0 = dim === X ? N : min(pts[0][1], pts[1][1]) + ], [ + e0 = dim === Y ? E : max(pts[0][0], pts[1][0]), + s0 = dim === X ? S : max(pts[0][1], pts[1][1]) + ]]; + if (points.length > 1) move(); } else { w0 = selection[0][0]; n0 = selection[0][1]; @@ -399,18 +411,26 @@ function brush(dim) { emit.start(); function moved(event) { - var point1 = pointer(event.touches ? event.touches[0] : event, that); - if (shifting && !lockX && !lockY) { - if (Math.abs(point1[0] - point[0]) > Math.abs(point1[1] - point[1])) lockY = true; - else lockX = true; + for (const p of event.changedTouches || [event]) { + for (const d of points) + if (d.identifier === p.identifier) d.cur = pointer(p, that); } - point = point1; + if (shifting && !lockX && !lockY && points.length === 1) { + const point = points[0]; + if (abs(point.cur[0] - point[0]) > abs(point.cur[1] - point[1])) + lockY = true; + else + lockX = true; + } + for (const point of points) + if (point.cur) point[0] = point.cur[0], point[1] = point.cur[1]; moving = true; noevent(event); move(); } function move() { + const point = points[0], point0 = point.point0; var t; dx = point[0] - point0[0]; @@ -419,20 +439,25 @@ function brush(dim) { switch (mode) { case MODE_SPACE: case MODE_DRAG: { - if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx; - if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy; + if (signX) dx = max(W - w0, min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx; + if (signY) dy = max(N - n0, min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy; break; } case MODE_HANDLE: { - if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0; - else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx; - if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0; - else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy; + if (points[1]) { + if (signX) w1 = max(W, min(E, points[0][0])), e1 = max(W, min(E, points[1][0])), signX = 1; + if (signY) n1 = max(N, min(S, points[0][1])), s1 = max(N, min(S, points[1][1])), signY = 1; + } else { + if (signX < 0) dx = max(W - w0, min(E - w0, dx)), w1 = w0 + dx, e1 = e0; + else if (signX > 0) dx = max(W - e0, min(E - e0, dx)), w1 = w0, e1 = e0 + dx; + if (signY < 0) dy = max(N - n0, min(S - n0, dy)), n1 = n0 + dy, s1 = s0; + else if (signY > 0) dy = max(N - s0, min(S - s0, dy)), n1 = n0, s1 = s0 + dy; + } break; } case MODE_CENTER: { - if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX)); - if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY)); + if (signX) w1 = max(W, min(E, w0 - dx * signX)), e1 = max(W, min(E, e0 + dx * signX)); + if (signY) n1 = max(N, min(S, n0 - dy * signY)), s1 = max(N, min(S, s0 + dy * signY)); break; } }