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