I have implemented a zooming treemap using D3.js. The treemap can be zoomed and panned I have limited the zooming function but cant limit the pan of the map. I want the panning to be only be inside the map rather than outside of it.
This is the current working implementation of my treemap : http://www.advbizclan.com/treemapt/
I want it like the zooming function which is only limited to the map the panning should also be limited only to the map.
The problem with your fiddle was the LoadType was incorrect. To fix this just click the Javascript button at the top of the Javascript window, and select No wrap- in <body> in the LOAD TYPE.
As for your problem. It's fairly simple. At the moment, you have this for your panning and zooming of your treemap :
.call(d3.behavior.zoom().scaleExtent([1, 3]).on("zoom", function() {
console.log(d3.event.translate)
chart.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")")
This uses the d3.event.translate to translate your svg. But you only want to pan in the y direction yes ? As d3.event.translatereturns an array of x and y coordinates, and the translate takes an x and y coordinate to translate by, just pass 0 for the x and d3.event.translate[1] (i.e the y coordinate) to the translate like so :
.on("zoom", function() {
chart.attr("transform", function(d) {
console.log(d3.event.translate);
return "translate(" + [0, d3.event.translate[1]] + ")" + " scale(" + d3.event.scale + ")"
})
}))
Now for the extent. You need to add limits to this, for my example I have just done this (you can easily implement your own limits) :
if(d3.event.translate[1] > 0 && d3.event.translate[1] < chartHeight/2 ){
console.log('move')
return "translate(" + [0, d3.event.translate[1]] + ")" + " scale(" + d3.event.scale + ")";
}
Here, you cant drag the map up, but you can drag it down half way.
Updated fiddle : https://jsfiddle.net/thatoneguy/w7em39wj/
Related
I've built a D3 force graph largely based on these really helpful examples.
I wanted to add pan and zoom functionality, which I tried to do using another example (looks like I can only include two links, but Google "d3 force zoom eyaler" to find it).
Unfortunately, when I zoom out on a graph that is larger than the initial SVG, I get something like this:
Result of dragging and dropping
Here's the relevant code:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().scaleExtent([0.5,2]).on("zoom", redraw));
function redraw() {
svg.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
How can I change the pan and zoom behaviour so that it scrolls and makes it possible to see the rest of the graph, rather than just allowing me to move the square that was originally visible?
OK, looks like I worked it out... you need to perform the transform on a <g> rather than on the SVG itself. So:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().scaleExtent([0.5,2]).on("zoom", redraw));
var g = svg.append("g"); // add <g>
function redraw() {
g.attr("transform", // perform transform on g, not svg
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
Just putting this here in case anyone else made the same mistake!
I've already spent too much time trying to figure this out.
My goal is to create d3 collapsible tree but for some reason when you zoom it, it moves the tree on position 0,0. I've already seen a few questions with similar problem such as this one d3.behavior.zoom jitters, shakes, jumps, and bounces when dragging but can't figure it out how to apply it to my situation.
I think this part is making a problem but I'm not sure how to change it to have the proper zooming functionality.
d3.select('g').transition()
.duration(duration)
.attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")")
zoomListener.scale(scale);
Here is my code: https://jsfiddle.net/ramo2600/y79r5dyk/11/
You are translating your zoomable g to position [100,100] but not telling the zoom d3.behavior.zoom() about it. So it starts from [0,0] and you see the "jump".
Modify your centerNode function to:
function centerNode(source) {
scale = zoomListener.scale();
// x = -source.y0;
y = -source.x0;
// x = x * scale + viewerWidth / 2;
x = 100;
y = 100;
// y = y * scale + viewerHeight / 2;
d3.select('g').transition()
.duration(duration)
.attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")")
zoomListener.scale(scale);
zoomListener.translate([x,y]); //<-- tell zoom about position
}
I tried to copy the behavior of the grid displayed here.
The code I have is here
Problem is, when I'm dragging or zooming, the grid is moving along with everything else, while it should not.
I suspect the problem is around this part :
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Indeed, when I remove the last line, the grid works fine (more or less) :
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
}
However, in this case, my visualization (the lines) don't move anymore:
How can I make my lines and the grid move the right way?
I have corrected your code
Working example here: http://jsfiddle.net/cyril123/p4cmx1kj/3/
You will need to update the lines also on zoom along with axis.
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
lines.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
I have a D3js map built with topojson.js.
var projection = d3.geo.mercator();
Everything works fine, but I am looking for an effect that I cannot manage to achieve. When, zooming the map I would like the pins over it to scale down, But if I scale it down, I need to recalculate their coordinates and I can't find the formula to do so.
Zoom handler
scale = d3.event.scale;
if (scale >= 1) {
main.attr("transform", "translate(" + d3.event.translate + ")
scale(" + d3.event.scale + ")");
}
else {
main.attr("transform", "translate(" + d3.event.translate + ")scale(1)");
}
//43x49 is the size initial pine size when scale = 1
var pins = main.select("#pins").selectAll("g").select("image")
.attr("width", function () {
return 43 - (43 * (scale - 1));
})
.attr("height", function () {
return 49 - (49 * (scale - 1));
})
.attr("x", function () {
//Calculate new image coordinates;
})
.attr("y", function () {
//Calculate new image coordinates;
});
My question is : how do I calculate x and y based on new scale?
I hope I am clear enough.
Thanks for your help
EDIT :
Calculation of initial pins coordinates :
"translate(" + (projection([d.lon, d.lat])[0] - 20) + ","
+ (projection([d.lon, d.lat])[1] - 45) + ")"
-20 and -45 to have the tip of the pin right on the target.
You need to "counter-scale" the pins, i.e. as main scales up/down you need to scale the pins down/up, in the opposite direction. The counter-scale factor is 1/scale, and you should apply it to each of the <g>s containing the pin image. This lets you remove your current width and height calculation from the <image> nodes (since the parent <g>'s scale will take care of it).
However, for this to work properly, you'll also need to remove the x and y attributes from the <image> and apply position via the counter-scaled parent <g> as well. This is necessary because if there's a local offset (which is the case when x and y are set on the <image>) that local offset gets scaled as the parent <g> is scaled, which makes the pin move to an incorrect location.
So:
var pinContainers = main.select("#pins").selectAll("g")
.attr("transform", function(d) {
var x = ... // don't know how you calculate x (since you didn't show it)
var y = ... // don't know how you calculate y
var unscale = 1/scale;
return "translate(" + x + " " + y + ") scale(" + unscale + ")";
})
pinContainers.select("image")
.attr("width", 43)
.attr("height", 49)
// you can use non-zero x and y to get the image to scale
// relative to some point other than the top-left of the
// image (such as the tip of the pin)
.attr("x", 0)
.attr("y", 0)
This question already has answers here:
How to disable double click zoom for d3.behavior.zoom?
(3 answers)
Closed 8 years ago.
I have a D3 Network Graph and I am trying to disable the Double Click zoom function.
I have it zooming using:
var zoom = d3.behavior.zoom().scaleExtent([minZoom, maxZoom]);
zoom.on("zoom", function() {
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
});
svg.call(zoom)
However I cant seem to be able to disable just the Double click zoom. When I use the code below it disables the zoom altogether.
var zoom = d3.behavior.zoom().scaleExtent([minZoom, maxZoom]);
zoom.on("zoom", function() {
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")").on("dblclick.zoom", null);
});
svg.call(zoom)
I have also tried calling
.on("dblclick.zoom", null)
on the svg element by itself and that doesnt work either.
Any help would be much appreciated.
You need to call
.on("dblclick.zoom", null)
after
svg.call(zoom)
i.e.
svg.call(zoom).on("dblclick.zoom", null);