diff --git a/README.md b/README.md index f784300a..ee14d98a 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), [Examples](https://observablehq.com/d/48ff4a64c477cc79) + +If *center* is a function, sets the center to the specified function, and returns the zoom behavior. If *center* is an array, sets the center to a constant function that returns the specified array. 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 the reference 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..b3c5d194 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, @@ -243,6 +248,7 @@ export default function() { if (g.wheel) { if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { g.mouse[1] = t.invert(g.mouse[0] = p); + g.mouse[2] = center.apply(this, arguments); } clearTimeout(g.wheel); } @@ -252,14 +258,14 @@ export default function() { // Otherwise, capture the mouse point and location at the start. else { - g.mouse = [p, t.invert(p)]; + g.mouse = [p, t.invert(p), center.apply(this, arguments)]; interrupt(this); g.start(); } noevent(event); g.wheel = setTimeout(wheelidled, wheelDelay); - g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent)); + g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[2], t.invert(g.mouse[2])), g.extent, translateExtent)); function wheelidled() { g.wheel = null; @@ -404,6 +410,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; };