Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
package.src.behavior.zoom.js Maven / Gradle / Ivy
import "../core/document";
import "../core/rebind";
import "../event/drag";
import "../event/event";
import "../event/mouse";
import "../event/touches";
import "../selection/selection";
import "../interpolate/zoom";
import "behavior";
d3.behavior.zoom = function() {
var view = {x: 0, y: 0, k: 1},
translate0, // translate when we started zooming (to avoid drift)
center0, // implicit desired position of translate0 after zooming
center, // explicit desired position of translate0 after zooming
size = [960, 500], // viewport size; required for zoom interpolation
scaleExtent = d3_behavior_zoomInfinity,
duration = 250,
zooming = 0,
mousedown = "mousedown.zoom",
mousemove = "mousemove.zoom",
mouseup = "mouseup.zoom",
mousewheelTimer,
touchstart = "touchstart.zoom",
touchtime, // time of last touchstart (to detect double-tap)
event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"),
x0,
x1,
y0,
y1;
// Lazily determine the DOM’s support for Wheel events.
// https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel
if (!d3_behavior_zoomWheel) {
d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel")
: "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel")
: (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll");
}
function zoom(g) {
g .on(mousedown, mousedowned)
.on(d3_behavior_zoomWheel + ".zoom", mousewheeled)
.on("dblclick.zoom", dblclicked)
.on(touchstart, touchstarted);
}
zoom.event = function(g) {
g.each(function() {
var dispatch = event.of(this, arguments),
view1 = view;
if (d3_transitionInheritId) {
d3.select(this).transition()
.each("start.zoom", function() {
view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state
zoomstarted(dispatch);
})
.tween("zoom:zoom", function() {
var dx = size[0],
dy = size[1],
cx = center0 ? center0[0] : dx / 2,
cy = center0 ? center0[1] : dy / 2,
i = d3.interpolateZoom(
[(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k],
[(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k]
);
return function(t) {
var l = i(t), k = dx / l[2];
this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k};
zoomed(dispatch);
};
})
.each("interrupt.zoom", function() {
zoomended(dispatch);
})
.each("end.zoom", function() {
zoomended(dispatch);
});
} else {
this.__chart__ = view;
zoomstarted(dispatch);
zoomed(dispatch);
zoomended(dispatch);
}
});
}
zoom.translate = function(_) {
if (!arguments.length) return [view.x, view.y];
view = {x: +_[0], y: +_[1], k: view.k}; // copy-on-write
rescale();
return zoom;
};
zoom.scale = function(_) {
if (!arguments.length) return view.k;
view = {x: view.x, y: view.y, k: null}; // copy-on-write
scaleTo(+_);
rescale();
return zoom;
};
zoom.scaleExtent = function(_) {
if (!arguments.length) return scaleExtent;
scaleExtent = _ == null ? d3_behavior_zoomInfinity : [+_[0], +_[1]];
return zoom;
};
zoom.center = function(_) {
if (!arguments.length) return center;
center = _ && [+_[0], +_[1]];
return zoom;
};
zoom.size = function(_) {
if (!arguments.length) return size;
size = _ && [+_[0], +_[1]];
return zoom;
};
zoom.duration = function(_) {
if (!arguments.length) return duration;
duration = +_; // TODO function based on interpolateZoom distance?
return zoom;
};
zoom.x = function(z) {
if (!arguments.length) return x1;
x1 = z;
x0 = z.copy();
view = {x: 0, y: 0, k: 1}; // copy-on-write
return zoom;
};
zoom.y = function(z) {
if (!arguments.length) return y1;
y1 = z;
y0 = z.copy();
view = {x: 0, y: 0, k: 1}; // copy-on-write
return zoom;
};
function location(p) {
return [(p[0] - view.x) / view.k, (p[1] - view.y) / view.k];
}
function point(l) {
return [l[0] * view.k + view.x, l[1] * view.k + view.y];
}
function scaleTo(s) {
view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
}
function translateTo(p, l) {
l = point(l);
view.x += p[0] - l[0];
view.y += p[1] - l[1];
}
function zoomTo(that, p, l, k) {
that.__chart__ = {x: view.x, y: view.y, k: view.k};
scaleTo(Math.pow(2, k));
translateTo(center0 = p, l);
that = d3.select(that);
if (duration > 0) that = that.transition().duration(duration);
that.call(zoom.event);
}
function rescale() {
if (x1) x1.domain(x0.range().map(function(x) { return (x - view.x) / view.k; }).map(x0.invert));
if (y1) y1.domain(y0.range().map(function(y) { return (y - view.y) / view.k; }).map(y0.invert));
}
function zoomstarted(dispatch) {
if (!zooming++) dispatch({type: "zoomstart"});
}
function zoomed(dispatch) {
rescale();
dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]});
}
function zoomended(dispatch) {
if (!--zooming) dispatch({type: "zoomend"}), center0 = null;
}
function mousedowned() {
var that = this,
dispatch = event.of(that, arguments),
dragged = 0,
subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended),
location0 = location(d3.mouse(that)),
dragRestore = d3_event_dragSuppress(that);
d3_selection_interrupt.call(that);
zoomstarted(dispatch);
function moved() {
dragged = 1;
translateTo(d3.mouse(that), location0);
zoomed(dispatch);
}
function ended() {
subject.on(mousemove, null).on(mouseup, null);
dragRestore(dragged);
zoomended(dispatch);
}
}
// These closures persist for as long as at least one touch is active.
function touchstarted() {
var that = this,
dispatch = event.of(that, arguments),
locations0 = {}, // touchstart locations
distance0 = 0, // distance² between initial touches
scale0, // scale when we started touching
zoomName = ".zoom-" + d3.event.changedTouches[0].identifier,
touchmove = "touchmove" + zoomName,
touchend = "touchend" + zoomName,
targets = [],
subject = d3.select(that),
dragRestore = d3_event_dragSuppress(that);
started();
zoomstarted(dispatch);
// Workaround for Chrome issue 412723: the touchstart listener must be set
// after the touchmove listener.
subject.on(mousedown, null).on(touchstart, started); // prevent duplicate events
// Updates locations of any touches in locations0.
function relocate() {
var touches = d3.touches(that);
scale0 = view.k;
touches.forEach(function(t) {
if (t.identifier in locations0) locations0[t.identifier] = location(t);
});
return touches;
}
// Temporarily override touchstart while gesture is active.
function started() {
// Listen for touchmove and touchend on the target of touchstart.
var target = d3.event.target;
d3.select(target).on(touchmove, moved).on(touchend, ended);
targets.push(target);
// Only track touches started on the same subject element.
var changed = d3.event.changedTouches;
for (var i = 0, n = changed.length; i < n; ++i) {
locations0[changed[i].identifier] = null;
}
var touches = relocate(),
now = Date.now();
if (touches.length === 1) {
if (now - touchtime < 500) { // dbltap
var p = touches[0];
zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
d3_eventPreventDefault();
}
touchtime = now;
} else if (touches.length > 1) {
var p = touches[0], q = touches[1],
dx = p[0] - q[0], dy = p[1] - q[1];
distance0 = dx * dx + dy * dy;
}
}
function moved() {
var touches = d3.touches(that),
p0, l0,
p1, l1;
d3_selection_interrupt.call(that);
for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
p1 = touches[i];
if (l1 = locations0[p1.identifier]) {
if (l0) break;
p0 = p1, l0 = l1;
}
}
if (l1) {
var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1,
scale1 = distance0 && Math.sqrt(distance1 / distance0);
p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
scaleTo(scale1 * scale0);
}
touchtime = null;
translateTo(p0, l0);
zoomed(dispatch);
}
function ended() {
// If there are any globally-active touches remaining, remove the ended
// touches from locations0.
if (d3.event.touches.length) {
var changed = d3.event.changedTouches;
for (var i = 0, n = changed.length; i < n; ++i) {
delete locations0[changed[i].identifier];
}
// If locations0 is not empty, then relocate and continue listening for
// touchmove and touchend.
for (var identifier in locations0) {
return void relocate(); // locations may have detached due to rotation
}
}
// Otherwise, remove touchmove and touchend listeners.
d3.selectAll(targets).on(zoomName, null);
subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
dragRestore();
zoomended(dispatch);
}
}
function mousewheeled() {
var dispatch = event.of(this, arguments);
if (mousewheelTimer) clearTimeout(mousewheelTimer);
else d3_selection_interrupt.call(this), translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch);
mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50);
d3_eventPreventDefault();
scaleTo(Math.pow(2, d3_behavior_zoomDelta() * 0.002) * view.k);
translateTo(center0, translate0);
zoomed(dispatch);
}
function dblclicked() {
var p = d3.mouse(this),
k = Math.log(view.k) / Math.LN2;
zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
}
return d3.rebind(zoom, event, "on");
};
var d3_behavior_zoomInfinity = [0, Infinity], // default scale extent
d3_behavior_zoomDelta, // initialized lazily
d3_behavior_zoomWheel;