I want to create the following:
Make a dynamic graph
It is Zoomable (Zooms at the center of currently seen display) (Zoom when certain buttons are clicked, mouse wheel are disabled for zoom)
Elements are draggable (When dragged it is not affected by force graph arrangement) (When elements are dragged outside of the svg the svg grows in size)
It has Scrollbar used as pan
So far I am already successful with
I have two problems with these combination items:
I would really need help for these two problems. I have not seen any example for zoom and scrollbar combination.
Here is the code.
function drawGraph(Data){
setDefault();
svg = d3.select("#graphingArea").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(zoom)
.on("dblclick.zoom", false)
.on("mousewheel.zoom", false)
.on("DOMMouseScroll.zoom", false) // disables older versions of Firefox
.on("wheel.zoom", false); // disables newer versions of Firefox;
//Needed for canvas to be dragged
rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
//Holds all that is to be dragged by the canvas
container = svg.append("g");
//Call zoom before drawing
svg.call(zoomUpdate);
//FOR DRAG
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
//Creating data that is drawn
populateD3Data(container, drag);
// Set data to be Force Arranged
force = self.force = d3.layout.force()
.nodes(nodes)
.links(links)
.distance(150)
.charge(-1000)
.size([width,height])
.start();
//Event to call arrange
force.on("tick", tick);
}
Zooming js:
var zoom = d3.behavior.zoom()
.scaleExtent([zoom_min_scale, zoom_max_scale])
.on("zoom", zoomed);
function zoomed() {
if(container != null && container != undefined) {
var translate = zoom.translate(),
scale = zoom.scale();
tx = Math.min(0, Math.max(width * (1 - scale), translate[0]));
ty = Math.min(0, Math.max(height * (1 - scale), translate[1]));
zoom.translate([tx, ty]);
container.attr("transform", "translate(" + [0,0] + ")scale(" + zoom.scale() + ")");
svg.attr("width", AreaWidth_ * zoom.scale());
svg.attr("height", AreaHeight_ * zoom.scale());
$("#graphingArea").scrollLeft(Math.abs(zoom.translate()[0]));
$("#graphingArea").scrollTop(Math.abs(zoom.translate()[1]));
}
}
//Button event for zoom in
d3.select("#zoom_in")
.on("click", zoomInOrOut);
//Button event for zoom out
d3.select("#zoom_out")
.on("click", zoomInOrOut);
//Gets the center of currently seen display
function interpolateZoom (translate, scale) {
return d3.transition().duration(1).tween("zoom", function () {
var iTranslate = d3.interpolate(zoom.translate(), translate),
iScale = d3.interpolate(zoom.scale(), scale);
return function (t) {
zoom
//Round number to nearest int because expected scale for now is whole number
.scale(Math.floor(iScale(t)))
.translate(iTranslate(t));
zoomed();
};
});
}
function zoomInOrOut() {
var direction = 1,
target_zoom = 1,
center = [graph_area_width / 2, graph_area_height / 2],
extent = zoom.scaleExtent(),
translate = zoom.translate(),
translate0 = [],
l = [],
view = {x: translate[0], y: translate[1], k: zoom.scale()};
d3.event.preventDefault();
direction = (this.id === 'zoom_in') ? 1 : -1;
target_zoom = zoom.scale() + (direction * zoom_scale);
if (target_zoom < extent[0] || target_zoom > extent[1]) { return false; }
translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
view.k = target_zoom;
l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y];
view.x += center[0] - l[0];
view.y += center[1] - l[1];
interpolateZoom([view.x, view.y], view.k);
}
function zoomUpdate() {
var target_zoom = 1,
center = [graph_area_width / 2, graph_area_height / 2],
extent = zoom.scaleExtent(),
translate = zoom.translate(),
translate0 = [],
l = [],
view = {x: translate[0], y: translate[1], k: zoom.scale()};
target_zoom = zoom.scale();
if (target_zoom < extent[0] || target_zoom > extent[1]) { return false; }
translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
view.k = target_zoom;
l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y];
view.x += center[0] - l[0];
view.y += center[1] - l[1];
interpolateZoom([view.x, view.y], view.k);
}
Here is my take on combining d3-zoom with scrollbars: https://stackblitz.com/edit/d3-pan-and-zoom
Apart from handling d3's zoom to update scrollbar positions, you also need to handle scrolling with scrollbars to update d3's internal zoom representation by calling translateTo()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With