d3/d3.behavior.js

145 строки
3.2 KiB
JavaScript

(function(){d3.behavior = {};
// TODO unbind zoom behavior?
// TODO unbind listener?
d3.behavior.zoom = function() {
var 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;
}
}
// 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 outer = d3.select("body").append("div")
.style("visibility", "hidden")
.style("position", "absolute")
.style("top", "-3000px")
.style("height", 0)
.style("overflow-y", "scroll")
.append("div")
.style("height", "2000px")
.node().parentNode;
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 || -e.detail;
if (delta) {
try {
outer.scrollTop = 1000;
outer.dispatchEvent(e);
delta = 1000 - outer.scrollTop;
} catch (error) {
// Derp! Hope for the best?
}
delta *= .005;
}
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;
};
})();