Permalink
Browse files

Allow multiple zoom behaviors to coexist.

Any event handlers on the window need to manage global state, as they will be
replaced when another zoom behavior is created.
  • Loading branch information...
1 parent 1885dd5 commit 06ecb617a272d3b23faaa73240c4f2ec3fdb265c @mbostock mbostock committed Jul 10, 2011
Showing with 304 additions and 290 deletions.
  1. +150 −143 d3.behavior.js
  2. +1 −1 d3.behavior.min.js
  3. +1 −1 d3.js
  4. +1 −1 d3.min.js
  5. +150 −143 src/behavior/zoom.js
  6. +1 −1 src/core/core.js
View
@@ -2,30 +2,9 @@
// TODO unbind zoom behavior?
// TODO unbind listener?
d3.behavior.zoom = function() {
- var x = 0,
- y = 0,
- z = 0,
- panning,
- zooming,
- locations = {}, // identifier -> location
- last = 0,
- target,
- targuments,
+ var xyz = [0, 0, 0],
event = d3.dispatch("zoom");
- // mousewheel events are totally broken!
- // https://bugs.webkit.org/show_bug.cgi?id=40441
- // not only that, but Chrome and Safari differ in re. to acceleration!
- var div = d3.select("body").append("div")
- .style("visibility", "hidden")
- .style("top", 0)
- .style("height", 0)
- .style("width", 0)
- .style("overflow-y", "scroll")
- .append("div")
- .style("height", "2000px")
- .node().parentNode;
-
function zoom() {
this
.on("mousedown.zoom", mousedown)
@@ -35,157 +14,185 @@ d3.behavior.zoom = function() {
.on("touchstart.zoom", touchstart);
d3.select(window)
- .on("mousemove.zoom", mousemove)
- .on("mouseup.zoom", mouseup)
- .on("touchmove.zoom", touchmove)
- .on("touchend.zoom", touchup);
+ .on("mousemove.zoom", d3_behavior_zoomMousemove)
+ .on("mouseup.zoom", d3_behavior_zoomMouseup)
+ .on("touchmove.zoom", d3_behavior_zoomTouchmove)
+ .on("touchend.zoom", d3_behavior_zoomTouchup);
}
- function location(point) {
- return [point[0] - x, point[1] - y, z];
+ // snapshot the local context for subsequent dispatch
+ function start() {
+ d3_behavior_zoomXyz = xyz;
+ d3_behavior_zoomDispatch = event.zoom.dispatch;
+ d3_behavior_zoomTarget = this;
+ d3_behavior_zoomArguments = arguments;
}
- // snapshot the target, data and index for subsequent dispatch
- function start() {
- target = this;
- targuments = arguments;
+ function mousedown() {
+ start.apply(this, arguments);
+ d3_behavior_zoomPanning = d3_behavior_zoomLocation(d3.svg.mouse(d3_behavior_zoomTarget));
+ d3.event.preventDefault();
+ window.focus();
+ }
+
+ // store starting mouse location
+ function mousewheel() {
+ start.apply(this, arguments);
+ if (!d3_behavior_zoomZooming) d3_behavior_zoomZooming = d3_behavior_zoomLocation(d3.svg.mouse(d3_behavior_zoomTarget));
+ d3_behavior_zoomTo(d3_behavior_zoomDelta() + xyz[2], d3.svg.mouse(d3_behavior_zoomTarget), d3_behavior_zoomZooming);
}
- // Note: Since we don't rotate, it's possible for the touches to become
- // slightly detached from their original positions. Thus, we recompute the
- // touch points on touchend as well as touchstart!
- function touchup() {
- var touches = d3.svg.touches(target),
- i = -1,
- n = touches.length,
- touch;
- while (++i < n) locations[(touch = touches[i]).identifier] = location(touch);
- return touches;
+ function dblclick() {
+ start.apply(this, arguments);
+ var mouse = d3.svg.mouse(d3_behavior_zoomTarget);
+ d3_behavior_zoomTo(d3.event.shiftKey ? Math.ceil(xyz[2] - 1) : Math.floor(xyz[2] + 1), mouse, d3_behavior_zoomLocation(mouse));
}
+ // doubletap detection
function touchstart() {
start.apply(this, arguments);
- var touches = touchup(),
+ var touches = d3_behavior_zoomTouchup(),
touch,
now = Date.now();
-
- // doubletap detection
- if ((touches.length === 1) && (now - last < 300)) {
- var touch = touches[0];
- zoomto(1 + Math.floor(z), touch, locations[touch.identifier]);
+ if ((touches.length === 1) && (now - d3_behavior_zoomLast < 300)) {
+ d3_behavior_zoomTo(1 + Math.floor(xyz[2]), touch = touches[0], d3_behavior_zoomLocations[touch.identifier]);
}
- last = now;
+ d3_behavior_zoomLast = now;
}
- function touchmove() {
- var touches = d3.svg.touches(target);
- switch (touches.length) {
-
- // single-touch pan
- case 1: {
- var touch = touches[0];
- zoomto(z, touch, locations[touch.identifier]);
- break;
- }
-
- // double-touch pan + zoom
- case 2: {
- var p0 = touches[0],
- p1 = touches[1],
- p2 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2],
- l0 = locations[p0.identifier],
- l1 = locations[p1.identifier],
- l2 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2, l0[2]];
- zoomto(Math.log(d3.event.scale) / Math.LN2 + l0[2], p2, l2);
- break;
- }
- }
- }
+ zoom.on = function(type, listener) {
+ event[type].add(listener);
+ return zoom;
+ };
- function mousedown() {
- start.apply(this, arguments);
- panning = location(d3.svg.mouse(target));
- d3.event.preventDefault();
- window.focus();
- }
+ return zoom;
+};
- function mousemove() {
- zooming = null;
- if (panning) zoomto(z, d3.svg.mouse(target), panning);
- }
+var d3_behavior_zoomDiv,
+ d3_behavior_zoomPanning,
+ d3_behavior_zoomZooming,
+ d3_behavior_zoomLocations = {}, // identifier -> location
+ d3_behavior_zoomLast = 0,
+ d3_behavior_zoomXyz,
+ d3_behavior_zoomDispatch,
+ d3_behavior_zoomTarget,
+ d3_behavior_zoomArguments;
+
+function d3_behavior_zoomLocation(point) {
+ return [
+ point[0] - d3_behavior_zoomXyz[0],
+ point[1] - d3_behavior_zoomXyz[1],
+ d3_behavior_zoomXyz[2]
+ ];
+}
+
+// detect the pixels that would be scrolled by this wheel event
+function d3_behavior_zoomDelta() {
- function mouseup() {
- if (panning) {
- mousemove();
- panning = null;
- }
+ // mousewheel events are totally broken!
+ // https://bugs.webkit.org/show_bug.cgi?id=40441
+ // not only that, but Chrome and Safari differ in re. to acceleration!
+ if (!d3_behavior_zoomDiv) {
+ d3_behavior_zoomDiv = d3.select("body").append("div")
+ .style("visibility", "hidden")
+ .style("top", 0)
+ .style("height", 0)
+ .style("width", 0)
+ .style("overflow-y", "scroll")
+ .append("div")
+ .style("height", "2000px")
+ .node().parentNode;
}
- function mousewheel() {
- start.apply(this, arguments);
+ var e = d3.event, delta;
+ try {
+ d3_behavior_zoomDiv.scrollTop = 1000;
+ d3_behavior_zoomDiv.dispatchEvent(e);
+ delta = 1000 - d3_behavior_zoomDiv.scrollTop;
+ } catch (error) {
+ delta = e.wheelDelta || -e.detail;
+ }
- // store starting mouse location
- if (!zooming) zooming = location(d3.svg.mouse(target));
-
- // detect the pixels that would be scrolled by this wheel event
- var e = d3.event, delta;
- try {
- div.scrollTop = 1000;
- div.dispatchEvent(e);
- delta = 1000 - div.scrollTop;
- } catch (error) {
- delta = e.wheelDelta || -e.detail;
+ return delta * .005;
+}
+
+// Note: Since we don't rotate, it's possible for the touches to become
+// slightly detached from their original positions. Thus, we recompute the
+// touch points on touchend as well as touchstart!
+function d3_behavior_zoomTouchup() {
+ var touches = d3.svg.touches(d3_behavior_zoomTarget),
+ i = -1,
+ n = touches.length,
+ touch;
+ while (++i < n) d3_behavior_zoomLocations[(touch = touches[i]).identifier] = d3_behavior_zoomLocation(touch);
+ return touches;
+}
+
+function d3_behavior_zoomTouchmove() {
+ var touches = d3.svg.touches(d3_behavior_zoomTarget);
+ switch (touches.length) {
+
+ // single-touch pan
+ case 1: {
+ var touch = touches[0];
+ d3_behavior_zoomTo(d3_behavior_zoomXyz[2], touch, d3_behavior_zoomLocations[touch.identifier]);
+ break;
}
- zoomto(delta * .005 + z, d3.svg.mouse(target), zooming);
+ // double-touch pan + zoom
+ case 2: {
+ var p0 = touches[0],
+ p1 = touches[1],
+ p2 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2],
+ l0 = d3_behavior_zoomLocations[p0.identifier],
+ l1 = d3_behavior_zoomLocations[p1.identifier],
+ l2 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2, l0[2]];
+ d3_behavior_zoomTo(Math.log(d3.event.scale) / Math.LN2 + l0[2], p2, l2);
+ break;
+ }
}
+}
- function dblclick() {
- start.apply(this, arguments);
- var mouse = d3.svg.mouse(target);
- zoomto(d3.event.shiftKey ? Math.ceil(z - 1) : Math.floor(z + 1), mouse, location(mouse));
- }
+function d3_behavior_zoomMousemove() {
+ d3_behavior_zoomZooming = null;
+ if (d3_behavior_zoomPanning) d3_behavior_zoomTo(d3_behavior_zoomXyz[2], d3.svg.mouse(d3_behavior_zoomTarget), d3_behavior_zoomPanning);
+}
- function zoomto(z1, x0, x1) {
- var k = Math.pow(2, (z = z1) - x1[2]);
- x = x0[0] - k * x1[0];
- y = x0[1] - k * x1[1];
- dispatch();
+function d3_behavior_zoomMouseup() {
+ if (d3_behavior_zoomPanning) {
+ d3_behavior_zoomMousemove();
+ d3_behavior_zoomPanning = null;
}
-
- function dispatch() {
- var o = d3.event, // Events can be reentrant (e.g., focus).
- k = Math.pow(2, z);
-
- d3.event = {
- scale: k,
- translate: [x, y],
- transform: function(sx, sy) {
- if (sx) transform(sx, x);
- if (sy) transform(sy, y);
- }
- };
-
- function transform(scale, o) {
- var domain = scale.__domain || (scale.__domain = scale.domain()),
- range = scale.range().map(function(v) { return (v - o) / k; });
- scale.domain(domain).domain(range.map(scale.invert));
- }
-
- try {
- event.zoom.dispatch.apply(target, targuments);
- } finally {
- d3.event = o;
+}
+
+function d3_behavior_zoomTo(z, x0, x1) {
+ var K = Math.pow(2, (d3_behavior_zoomXyz[2] = z) - x1[2]),
+ x = d3_behavior_zoomXyz[0] = x0[0] - K * x1[0],
+ y = d3_behavior_zoomXyz[1] = x0[1] - K * x1[1],
+ o = d3.event, // Events can be reentrant (e.g., focus).
+ k = Math.pow(2, z);
+
+ d3.event = {
+ scale: k,
+ translate: [x, y],
+ transform: function(sx, sy) {
+ if (sx) transform(sx, x);
+ if (sy) transform(sy, y);
}
+ };
- o.preventDefault();
+ function transform(scale, o) {
+ var domain = scale.__domain || (scale.__domain = scale.domain()),
+ range = scale.range().map(function(v) { return (v - o) / k; });
+ scale.domain(domain).domain(range.map(scale.invert));
}
- zoom.on = function(type, listener) {
- event[type].add(listener);
- return zoom;
- };
+ try {
+ d3_behavior_zoomDispatch.apply(d3_behavior_zoomTarget, d3_behavior_zoomArguments);
+ } finally {
+ d3.event = o;
+ }
- return zoom;
-};
+ o.preventDefault();
+}
})();
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View
2 d3.js
@@ -1,4 +1,4 @@
-(function(){d3 = {version: "1.27.0"}; // semver
+(function(){d3 = {version: "1.27.1"}; // semver
if (!Date.now) Date.now = function() {
return +new Date;
};
View

Large diffs are not rendered by default.

Oops, something went wrong.
Oops, something went wrong.

0 comments on commit 06ecb61

Please sign in to comment.