d3/d3.behavior.js

132 строки
3.0 KiB
JavaScript

(function(){d3.behavior = {};
// TODO unbind zoom behavior?
// TODO unbind listener?
d3.behavior.zoom = function() {
// https://bugs.webkit.org/show_bug.cgi?id=40441
var bug40441 = /WebKit\/533/.test(navigator.userAgent) ? -1 : 0,
bug40441Last = 0,
x = 0,
y = 0,
z = 0,
listeners = [],
pan,
zoom;
function zoom() {
var container = this
.on("mousedown", mousedown)
.on("mousewheel", mousewheel)
.on("DOMMouseScroll", mousewheel)
.on("dblclick", mousewheel);
d3.select(window)
.on("mousemove", mousemove)
.on("mouseup", mouseup);
}
function mousedown(d, i) {
pan = {
x0: x - d3.event.clientX,
y0: y - d3.event.clientY,
target: this,
data: d,
index: i
};
d3.event.preventDefault();
window.focus(); // TODO focusableParent
}
function mousemove() {
zoom = null;
if (pan) {
x = d3.event.clientX + pan.x0;
y = d3.event.clientY + pan.y0;
dispatch.call(pan.target, pan.data, pan.index);
}
}
function mouseup() {
if (pan) {
mousemove();
pan = null;
}
}
function mousewheel(d, i) {
var e = d3.event;
// initialize the mouse location for zooming (to avoid drift)
if (!zoom) {
var p = d3.svg.mouse(this.nearestViewportElement || this);
zoom = {
x0: x,
y0: y,
z0: z,
x1: x - p[0],
y1: y - p[1]
};
}
// adjust zoom level
if (e.type == "dblclick") {
z = e.shiftKey ? Math.ceil(z - 1) : Math.floor(z + 1);
} else {
var delta = (e.wheelDelta / 120 || -e.detail) * .1;
/* Detect fast & large wheel events on WebKit. */
if (bug40441 < 0) {
var now = Date.now(), since = now - bug40441Last;
if ((since > 9) && (Math.abs(e.wheelDelta) / since >= 50)) bug40441 = 1;
bug40441Last = now;
}
if (bug40441 == 1) delta *= .03;
z += delta;
}
// adjust x and y to center around mouse location
var k = Math.pow(2, z - zoom.z0) - 1;
x = zoom.x0 + zoom.x1 * k;
y = zoom.y0 + zoom.y1 * k;
// dispatch redraw
dispatch.call(this, d, i);
}
function dispatch(d, i) {
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 {
for (var j = 0, m = listeners.length; j < m; j++) {
listeners[j].call(this, d, i);
}
} finally {
d3.event = o;
}
}
zoom.on = function(type, listener) {
if (type == "zoom") listeners.push(listener);
return zoom;
};
return zoom;
};
})()