How do I fix zooming and panning in my cluster bundle graph? - javascript

I've created a hierarchical edge bundling graph with some data and after trying to implement zooming and dragging on the graph I've run into some issues.
Here is a similar working jsfiddle of what I have so far: https://jsfiddle.net/hnjvxd48/1/
and the relevant code:
var zoom = d3.behavior.zoom()
.scaleExtent([0,8])
.on("zoom", zoomhandler);
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select(".container").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")")
.call(zoom)
.call(drag);
function zoomhandler(){
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
You'll notice:
1) Dragging and zooming only occur on the outer edges and not the inner part of the graph.
2) Dragging the graph around causes flickering and centering of the graph to change and become cut off.
3) Zooming (done via mouse scroll wheel) also centers the graph incorrectly and places it in an unpredictable position, partly out of the view port.
4) Attempting to drag the graph after it has been zoomed out causes it to flicker and disappear.
What's causing these issues and how can I fix them? How can I give my graph (which is much bigger than the sample one I provided) an initially "zoomed out" state and perhaps trigger the zooming functionality using a button click event rather than the native scroll wheel implementation?

The big thing to notice here is that the drag functions are actually redundant. In this (http://bl.ocks.org/mbostock/6123708) d3 drag + zoom example, they're being used to move individual 'dots'. You want to move the whole graph at once, and this is handled by the 'translate' portion of the 'zoomhandler' function you've included.
Here's a working fiddle: https://jsfiddle.net/14f9f4k3/1/
And the key code that with changes noted in comments:
var zoom = d3.behavior.zoom()
.scaleExtent([0,8])
.on("zoom", zoomhandler);
//added another group as a child of the group having zoom called on it w/ id 'draggroup' to append nodes and links to
var svg = d3.select(".container").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")")
.call(zoom)
.append('g')
.attr('id','draggroup');
//added a rect behind the other elements to make an easy target for the pointer
d3.select('#draggroup')
.append('rect')
.attr("transform", "translate(" + -radius + "," + -radius + ")")
.attr('width',diameter)
.attr('height',diameter)
.attr('fill','#fff');
//no need for separate drag functions, translate and scale here do what you want
function zoomhandler(){
svg.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
}
//append the links and nodes to the group we created above instead of the base svg
var link = d3.select('#draggroup').append("g").selectAll(".link"),
node = d3.select('#draggroup').append("g").selectAll(".node");

Related

d3js,when mouse drag the blank area,the force graph keeps jumping

I have fllowing questions.
I used force graph and add zoom for it.
But when mouse drag the blank area,the force graph keeps jumping.
like this
How to make the graph do not jump?Thanks.
(I feel very sorry for my English.Thank you again.)
This is my zoom code.
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
function zoomed() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
I added a g element to your base SVG to group everything together (also appended any other elements to the g element instead of the svg), then in the zoomed() method, performed transforms on that g element instead of on the svg element:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")");
Zoom method:
function zoomed() {
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Also, when setting up node listeners, stop event propagation for the 'mousedown' event, like so:
var node = g.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", click)
.on("dblclick", dblclick)
.on("mousedown", function(d) {
d3.event.stopPropagation();
})
.call(force.drag)
.on('click', connectedNodes);
Here's the working example.

Centering and allowing drag with d3 zoom

I'm working off of this Block. My code has departed quite a bit from it, but I've just run into an issue (that I confirmed holds on the original Block).
When I add zoom/pan, the center of my force graph moves to the top left of the screen (which I can then pan down to center). And, I can't drag any nodes.
The zoom code I added is on the svg creation:
var svg = d3.select("body")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.call(d3.behavior.zoom().on("zoom", function () {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")")
}))
.append("g");
Any thoughts on how to start the graph center and allow for dragging nodes?

Zoom/Pan D3.js Polar Scatter Plot

I created a polar scatter plot using D3.js (based on this post) .
I would like to add the functionality to zoom and pan. I've seen examples for rectangular plots, but nothing for zooming/panning on circular plots.
I am just a beginner with using D3 so I'm a little lost. Can anyone help/offer suggestions?
I'm not entirely sure what your goal is, but I tried something below.
First you should add zoom behaviour. I used the r scale for both your x and y directions like:
var zoomBeh = d3.behavior.zoom()
.x(r)
.y(r)
.on("zoom", zoom);
And call the zoom behaviour into your svg:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.call(zoomBeh);
Finally you should make a zoom function.
function zoom() {
var t = svg.transition().duration(750);
svg.selectAll(".point").transition(t)
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
})
}
Here is an updated fiddle. It's a little bit staggering, I'm not sure why yet.

In D3 how to enable zooming only for particular path?

I created a d3 geomap. In that i enabled zooming feature by using d3.behaviour.zoom().
Actually, zooming part is working fine.But my problem is i need to zoom only background layer.
I mean i created a world map. In that over the each country i created a circle. So while zooming i need to increase the world map size not that circle. I need to show same size of circle always.
Please help me to solve this .Also, i added fiddle link below.Kindly take a look and help me.
Fiddle Link - http://jsfiddle.net/sam0kqvx/39/
The zoom behaviour does what you tell it to. In your fiddle:
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Notice that scaling and translation is performed over the whole svg. You need to define another variable that has .countries without .dots and perform the zoom only on .countries
First, apply the zoom only on the <g> that contain .countries
var countries = svg.append("g") //apply zoom here.
.call(d3.behavior.zoom().scaleExtent([1, 8])
.on("zoom", zoom))
.selectAll(".countries")
.data(topojson.feature(world, world.objects.countries).features)
.enter()
Then, in zoom function, scale and translate only the selected elements this
function zoom() {
d3.select(this).attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Here is your updated fiddle
Note:
Disabling the zoom behaviour from the circles will also disable panning, which means that the user can slide the map from under the circles. You need to handle this as well.
=============
Another solution is to reverse scale the circles sizes in the zoom function, as mentioned in the comment.
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
svg.selectAll("circle").attr("r", 6/d3.event.scale);
}
Here is the fiddle for it.
If circle sizes are variable:
You can store the "true radius" is a separate attribute and replace 6/d3.event.scale by trueR/d3.event.scale
Here are the 2 updates in your fiddle.
//Store the original raduis is a "trueR" attributes
svg.selectAll(".dots")
.data(topojson.feature(world, world.objects.countries).features)
.enter()
.append("circle")
.attr("r",function(d,i){
radius = Math.random()*20;
return radius;
})
.attr("trueR", function(d){ return d3.select(this).attr("r")})
.attr("fill","black")
.attr("transform",function(d){
var p = projection(d3.geo.centroid(d));
return "translate("+p+")";
});
//Use "trueR" in the zoom() function
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
svg.selectAll("circle").attr("r", function(d){
return (d3.select(this).attr("trueR"))/d3.event.scale;
});
}
Update fiddle

Having trouble panning and zooming on my d3 map

I have this map in d3
http://107.170.20.64/
that renders topojson with a custom projection and path, like this
var projection = d3.geo.mercator().translate([width / 2, height / 2]).scale(width * 185).center([-89.99, 29.975]);
var path = d3.geo.path().projection(projection);
I am trying to adapt it so that it pans and zooms using Bostock's tutorial. Here is the function that fires once the topojson loads (showing my adaptations of Bostock's method):
function ready(error, us) {
var zoom = d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.scaleExtent([1, 8])
.on("zoom", zoomed);
var features = svg.append("g");
features.append("g")
.attr("class", "precincts")
.selectAll("path")
.data(topojson.feature(us, us.objects.orleansgeojson).features)
.enter().append("path")
.attr("class", (function(d) {
return wards.get(d.id) + " precinct";
}))
.attr("title", (function(d) {
return votesone.get(d.id) + "-" + votestwo.get(d.id);
}))
.attr("id", function(d) {
return d.id;
})
.attr("d", path);
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.call(zoom);
function zoomed() {
features.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
features.select(".precinct").style("stroke-width", 1.5 / d3.event.scale + "px");
}
Somehow, zoomed is never getting called. If I set a breakpoint on zoomed it never catches. I think that the final append to svg calls zoom which somehow sets up a d3 behavior that creates listeners for mouse events and calls the zoomed function. That's what I understand so far about what is going on (clarification or detailed answers would be great). Is the problem that the listeners are not getting set? If so, how do I debug why they are not getting set? The overlay shows up in my svg -- it just does not seem to be picking up mouse events.
The problem in your case is unrelated to the zoom behaviour. You're setting the z-index of the div containing the map to be -1. This means that it's behind the containing div, which gets all the mouse events. So the map is "obscured" by the element that contains it.
To fix, either set the z-index of the map div to be higher than -1, or set the z-index of all the containing elements (including the body) to be -1 or less.

Categories

Resources