diff --git a/README.md b/README.md index f784300a..01bb6864 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,18 @@ function constrain(transform, extent, translateExtent) { The constraint function must return a [*transform*](#zoom-transforms) given the current *transform*, [viewport extent](#zoom_extent) and [translate extent](#zoom_translateExtent). The default implementation attempts to ensure that the viewport extent does not go outside the translate extent. +# zoom.center([center]) · [Source](https://github.com/d3/d3-zoom/blob/master/src/zoom.js) + +If *center* is specified, sets the center to the specified function and returns the zoom behavior. If *center* is not specified, returns the current center, which defaults to the pointer’s position relative to the current DOM element: + +```js +function center(event) { + return d3.pointer(event, this); +} +``` + +The center is passed the current event (`event`) and datum `d`, with the `this` context as the current DOM element. It must return a position as [*x*, *y*]. + # zoom.filter([filter]) · [Source](https://github.com/d3/d3-zoom/blob/master/src/zoom.js) If *filter* is specified, sets the filter to the specified function and returns the zoom behavior. If *filter* is not specified, returns the current filter, which defaults to: diff --git a/src/zoom.js b/src/zoom.js index fa959025..f8733ceb 100644 --- a/src/zoom.js +++ b/src/zoom.js @@ -13,6 +13,10 @@ function defaultFilter(event) { return !event.ctrlKey && !event.button; } +function defaultCenter(event) { + return pointer(event, this); +} + function defaultExtent() { var e = this; if (e instanceof SVGElement) { @@ -51,6 +55,7 @@ function defaultConstrain(transform, extent, translateExtent) { export default function() { var filter = defaultFilter, + center = defaultCenter, extent = defaultExtent, constrain = defaultConstrain, wheelDelta = defaultWheelDelta, @@ -236,7 +241,7 @@ export default function() { var g = gesture(this, args).event(event), t = this.__zoom, k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))), - p = pointer(event); + p = center.apply(this, arguments); // If the mouse is in the same location as before, reuse it. // If there were recent wheel events, reset the wheel idle timeout. @@ -271,7 +276,7 @@ export default function() { if (touchending || !filter.apply(this, arguments)) return; var g = gesture(this, args, true).event(event), v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true), - p = pointer(event, currentTarget), + p = center.apply(currentTarget, arguments), currentTarget = event.currentTarget, x0 = event.clientX, y0 = event.clientY; @@ -289,7 +294,7 @@ export default function() { g.moved = dx * dx + dy * dy > clickDistance2; } g.event(event) - .zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent)); + .zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = center.apply(currentTarget, arguments), g.mouse[1]), g.extent, translateExtent)); } function mouseupped(event) { @@ -303,7 +308,7 @@ export default function() { function dblclicked(event, ...args) { if (!filter.apply(this, arguments)) return; var t0 = this.__zoom, - p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this), + p0 = center.call(this, event.changedTouches ? event.changedTouches[0] : event, ...args), p1 = t0.invert(p0), k1 = t0.k * (event.shiftKey ? 0.5 : 2), t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent); @@ -322,7 +327,7 @@ export default function() { nopropagation(event); for (i = 0; i < n; ++i) { - t = touches[i], p = pointer(t, this); + t = touches[i], p = center.call(this, t, ...args); p = [p, this.__zoom.invert(p), t.identifier]; if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting; else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0; @@ -347,7 +352,7 @@ export default function() { if (touchstarting) touchstarting = clearTimeout(touchstarting); g.taps = 0; for (i = 0; i < n; ++i) { - t = touches[i], p = pointer(t, this); + t = touches[i], p = center.call(this, t, ...args); if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p; else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p; } @@ -404,6 +409,10 @@ export default function() { return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), zoom) : touchable; }; + zoom.center = function(_) { + return arguments.length ? (center = typeof _ === "function" ? _ : constant([+_[0], +_[1]]), zoom) : center; + }; + zoom.extent = function(_) { return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; };