I'm trying to change the cursor when I drag on a SVG but the cursor change only fires when the mouse button is released. Works perfectly, however, in Firefox.
I'm using Chrome Version 50.0.2661.102 m.
This is the jQuery I'm using:
$('#map')
.mousedown(function(){
$(this).css( 'cursor', 'move' );
})
.mouseup(function(){
$(this).css( 'cursor', 'auto' );
});
BUT the code snippet I added below actually works as it should, yet the fiddle I created doesn't, nor does my actual code on the site I'm building.
How can I do this differently for Chrome so it works always?
var graph = {
"nodes":[
{"name":"1","rating":90,"id":2951},
{"name":"2","rating":80,"id":654654},
{"name":"3","rating":80,"id":6546544},
{"name":"4","rating":1,"id":68987978},
{"name":"5","rating":1,"id":9878933},
{"name":"6","rating":1,"id":6161},
{"name":"7","rating":1,"id":64654},
{"name":"8","rating":20,"id":354654},
{"name":"9","rating":50,"id":8494},
{"name":"10","rating":1,"id":6846874},
{"name":"11","rating":1,"id":5487},
{"name":"12","rating":80,"id":"parfum_kenzo"},
{"name":"13","rating":1,"id":65465465},
{"name":"14","rating":90,"id":"jungle_de_kenzo"},
{"name":"15","rating":20,"id":313514},
{"name":"16","rating":40,"id":36543614},
{"name":"17","rating":100,"id":"Yann_YA645"},
{"name":"18","rating":1,"id":97413},
{"name":"19","rating":1,"id":97414},
{"name":"20","rating":100,"id":976431231},
{"name":"21","rating":1,"id":9416},
{"name":"22","rating":1,"id":998949},
{"name":"23","rating":100,"id":984941},
{"name":"24","rating":100,"id":"99843"},
{"name":"25","rating":1,"id":94915},
{"name":"26","rating":1,"id":913134},
{"name":"27","rating":1,"id":9134371}
],
"links":[
{"source":6,"target":5,"value":6, "label":"publishedOn"},
{"source":8,"target":5,"value":6, "label":"publishedOn"},
{"source":7,"target":1,"value":4, "label":"containsKeyword"},
{"source":8,"target":10,"value":3, "label":"containsKeyword"},
{"source":7,"target":14,"value":4, "label":"publishedBy"},
{"source":8,"target":15,"value":6, "label":"publishedBy"},
{"source":9,"target":1,"value":6, "label":"depicts"},
{"source":10,"target":1,"value":6, "label":"depicts"},
{"source":16,"target":1,"value":6, "label":"manageWebsite"},
{"source":16,"target":2,"value":5, "label":"manageWebsite"},
{"source":16,"target":3,"value":6, "label":"manageWebsite"},
{"source":16,"target":4,"value":6, "label":"manageWebsite"},
{"source":19,"target":18,"value":2, "label":"postedOn"},
{"source":18,"target":1,"value":6, "label":"childOf"},
{"source":17,"target":19,"value":8, "label":"describes"},
{"source":18,"target":11,"value":6, "label":"containsKeyword"},
{"source":17,"target":13,"value":3, "label":"containsKeyword"},
{"source":20,"target":13,"value":3, "label":"containsKeyword"},
{"source":20,"target":21,"value":3, "label":"postedOn"},
{"source":22,"target":20,"value":3, "label":"postedOn"},
{"source":23,"target":21,"value":3, "label":"manageWebsite"},
{"source":23,"target":24,"value":3, "label":"manageWebsite"},
{"source":23,"target":25,"value":3, "label":"manageWebsite"},
{"source":23,"target":26,"value":3, "label":"manageWebsite"}
]
}
var margin = {top: -5, right: -5, bottom: -5, left: -5};
var width = 500 - margin.left - margin.right,
height = 400- margin.top - margin.bottom;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([width + margin.left + margin.right, height + margin.top + margin.bottom]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select("#map").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "svg")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
.call(zoom);
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
var container = svg.append("g");
//d3.json('http://blt909.free.fr/wd/map2.json', function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.call(drag);
node.append("circle")
.attr("r", function(d) { return d.weight * 2+ 12; })
.style("fill", function(d) { return color(1/d.rating); });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
var linkedByIndex = {};
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index];
}
node.on("mouseover", function(d){
node.classed("node-active", function(o) {
thisOpacity = isConnected(d, o) ? true : false;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.classed("link-active", function(o) {
return o.source === d || o.target === d ? true : false;
});
d3.select(this).classed("node-active", true);
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", (d.weight * 2+ 12)*1.5);
})
.on("mouseout", function(d){
node.classed("node-active", false);
link.classed("link-active", false);
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", d.weight * 2+ 12);
});
function dottype(d) {
d.x = +d.x;
d.y = +d.y;
return d;
}
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
force.start();
}
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);
}
$('#map')
.mousedown(function(){
$(this).css( 'cursor', 'move' );
})
.mouseup(function(){
$(this).css( 'cursor', 'auto' );
});
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.node-active{
stroke: #555;
stroke-width: 1.5px;
}
.link {
stroke: #555;
stroke-opacity: .3;
}
.link-active {
stroke-opacity: 1;
}
.overlay {
fill: none;
pointer-events: all;
}
#map{
border: 2px #555 dashed;
width:500px;
height:400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<body>
<div id="map"></div>
</body>
(sample SVG code borrowed from http://jsfiddle.net/JSDavi/qvco2Ljy/)
If anyone else comes up against this...this was my scenario:
If you have your Chrome inspector open, that is what causes this to fail, if you close the inspector for the page you're creating this effect on, it works perfectly.
You have:
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
force.start();
}
That call to stopPropagation seems to prevent mouse down event. Unfortunately, changing it to preventDefault changes how drag&drop works (it moves whole graph around, instead of moving single nodes).
If it is only the cursor you need to change, why not use CSS for that? You already seem to change class of node to dragging, so you should be able to simply use following CSS:
.node.dragging {
cursor: move;
}
If you want cursor to change also when whole graph is dragged, you can add dragged class to the #map too:
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
$("#map").addClass("dragging");
d3.select(this).classed("dragging", true);
force.start();
}
// ...
function dragended(d) {
$("#map").removeClass("dragging");
d3.select(this).classed("dragging", false);
}
and then add CSS for that:
#map.dragging {
cursor: move;
}
var graph = {
"nodes":[
{"name":"1","rating":90,"id":2951},
{"name":"2","rating":80,"id":654654},
{"name":"3","rating":80,"id":6546544},
{"name":"4","rating":1,"id":68987978},
{"name":"5","rating":1,"id":9878933},
{"name":"6","rating":1,"id":6161},
{"name":"7","rating":1,"id":64654},
{"name":"8","rating":20,"id":354654},
{"name":"9","rating":50,"id":8494},
{"name":"10","rating":1,"id":6846874},
{"name":"11","rating":1,"id":5487},
{"name":"12","rating":80,"id":"parfum_kenzo"},
{"name":"13","rating":1,"id":65465465},
{"name":"14","rating":90,"id":"jungle_de_kenzo"},
{"name":"15","rating":20,"id":313514},
{"name":"16","rating":40,"id":36543614},
{"name":"17","rating":100,"id":"Yann_YA645"},
{"name":"18","rating":1,"id":97413},
{"name":"19","rating":1,"id":97414},
{"name":"20","rating":100,"id":976431231},
{"name":"21","rating":1,"id":9416},
{"name":"22","rating":1,"id":998949},
{"name":"23","rating":100,"id":984941},
{"name":"24","rating":100,"id":"99843"},
{"name":"25","rating":1,"id":94915},
{"name":"26","rating":1,"id":913134},
{"name":"27","rating":1,"id":9134371}
],
"links":[
{"source":6,"target":5,"value":6, "label":"publishedOn"},
{"source":8,"target":5,"value":6, "label":"publishedOn"},
{"source":7,"target":1,"value":4, "label":"containsKeyword"},
{"source":8,"target":10,"value":3, "label":"containsKeyword"},
{"source":7,"target":14,"value":4, "label":"publishedBy"},
{"source":8,"target":15,"value":6, "label":"publishedBy"},
{"source":9,"target":1,"value":6, "label":"depicts"},
{"source":10,"target":1,"value":6, "label":"depicts"},
{"source":16,"target":1,"value":6, "label":"manageWebsite"},
{"source":16,"target":2,"value":5, "label":"manageWebsite"},
{"source":16,"target":3,"value":6, "label":"manageWebsite"},
{"source":16,"target":4,"value":6, "label":"manageWebsite"},
{"source":19,"target":18,"value":2, "label":"postedOn"},
{"source":18,"target":1,"value":6, "label":"childOf"},
{"source":17,"target":19,"value":8, "label":"describes"},
{"source":18,"target":11,"value":6, "label":"containsKeyword"},
{"source":17,"target":13,"value":3, "label":"containsKeyword"},
{"source":20,"target":13,"value":3, "label":"containsKeyword"},
{"source":20,"target":21,"value":3, "label":"postedOn"},
{"source":22,"target":20,"value":3, "label":"postedOn"},
{"source":23,"target":21,"value":3, "label":"manageWebsite"},
{"source":23,"target":24,"value":3, "label":"manageWebsite"},
{"source":23,"target":25,"value":3, "label":"manageWebsite"},
{"source":23,"target":26,"value":3, "label":"manageWebsite"}
]
}
var margin = {top: -5, right: -5, bottom: -5, left: -5};
var width = 500 - margin.left - margin.right,
height = 400- margin.top - margin.bottom;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([width + margin.left + margin.right, height + margin.top + margin.bottom]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select("#map").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "svg")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
.call(zoom);
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
var container = svg.append("g");
//d3.json('http://blt909.free.fr/wd/map2.json', function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.call(drag);
node.append("circle")
.attr("r", function(d) { return d.weight * 2+ 12; })
.style("fill", function(d) { return color(1/d.rating); });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
var linkedByIndex = {};
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index];
}
node.on("mouseover", function(d){
node.classed("node-active", function(o) {
thisOpacity = isConnected(d, o) ? true : false;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.classed("link-active", function(o) {
return o.source === d || o.target === d ? true : false;
});
d3.select(this).classed("node-active", true);
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", (d.weight * 2+ 12)*1.5);
})
.on("mouseout", function(d){
node.classed("node-active", false);
link.classed("link-active", false);
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", d.weight * 2+ 12);
});
function dottype(d) {
d.x = +d.x;
d.y = +d.y;
return d;
}
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
$("#map").addClass("dragging");
d3.select(this).classed("dragging", true);
force.start();
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
$("#map").removeClass("dragging");
d3.select(this).classed("dragging", false);
}
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.node.dragging,
#map.dragging {
cursor: move;
}
.node-active{
stroke: #555;
stroke-width: 1.5px;
}
.link {
stroke: #555;
stroke-opacity: .3;
}
.link-active {
stroke-opacity: 1;
}
.overlay {
fill: none;
pointer-events: all;
}
#map{
border: 2px #555 dashed;
width:500px;
height:400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<body>
<div id="map"></div>
</body>
Related
I want to make a draggable <g> element containing a <circle> and a <text> but I failed.
Now I have this code:
var margin = {top: -5, right: -5, bottom: -5, left: -5},
width = 1960 - margin.left - margin.right,
height = 1500 - margin.top - margin.bottom;
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var dotContainer = svg.append("g")
.attr("class", "dotContainer")
.datum({x:220, y:120})
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.call(drag);
var dot = dotContainer.append("circle")
.attr("class", "dot")
.datum({x:220, y:120})
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 5).call(drag);
var text = dotContainer.append("text")
.datum({x:220, y:120})
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.text('Title');
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);
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<circle> element is perfectly draggable, ok. But I need to apply dragging to the whole <g> element to drag my circle together with the <text> element.
How can I do this?
When I apply call(drag) to dotContainer like this
dotContainer.call(drag);
then nothing works.
You should rewrite your dotContainer variable this way:
var dotContainer = svg.append("g")
.attr("class", "dotContainer")
.datum({x:220, y:120})
.attr("transform", function(d) { return 'translate(' + d.x + ' '+ d.y + ')'; })
.call(drag);
Remove .call(drag) for dot variable and rewrite dragged function like this:
function dragged(d) {
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("transform", function(d,i){
return "translate(" + [ d.x,d.y ] + ")"
});
}
Thus we use transform attribute for an initial container position and for the position update during the dragging.
Check demo in the hidden snippet:
var margin = {top: -5, right: -5, bottom: -5, left: -5},
width = 1960 - margin.left - margin.right,
height = 1500 - margin.top - margin.bottom;
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var dotContainer = svg.append("g")
.attr("class", "dotContainer")
.datum({x:20, y:20})
.attr("transform", function(d) { return 'translate(' + d.x + ' '+ d.y + ')'; })
.call(drag);
var dot = dotContainer.append("circle")
.attr("class", "dot")
.datum({x:20, y:20})
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 5)
var text = dotContainer.append("text")
.datum({x:20, y:20})
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.text('Title');
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
}
function dragged(d) {
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("transform", function(d,i){
return "translate(" + [ d.x,d.y ] + ")"
});
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
.dotContainer {
cursor: move;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
var drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault;
start_x_old = +d3.event.x;
}
function dragged(e) {
xDrag.attr("transform", "translate(" + [( d3.event.x - start_x_old), 0] + ")");
xDrag1.attr("transform", "translate(" + [(d3.event.x - start_x_old), height] + ")");
}
function dragended(d) {
start_x_new = event.clientX;
console.log("old`enter code here`", start_x_old);
console.log("new", start_x_new);
//for future use
}`
This is the code that i wrote for drag where xDrag and xDrag1 are elements on which drag is being called. For the entire working code visit the fiddle https://jsfiddle.net/zaarkoevilor/vzqu1wrc/15/
You don't need to play around with start_x_old and start_x_new...
Just set a datum to your selections:
var xDrag = svg.selectAll(".serie")
.datum({x: 0})
.call(drag);
var xDrag1 = svg.selectAll(".axis--x")
.datum({x: 0})
.call(drag);
And use that in your drag:
function dragged(d) {
xDrag.attr("transform", "translate(" + [(d.x = d3.event.x), 0] + ")");
xDrag1.attr("transform", "translate(" + [(d.x = d3.event.x), height] + ")");
}
Here is your updated fiddle: https://jsfiddle.net/z0d8xpjg/
And the same code in the Stack snippet:
var start_x_old, start_x_new = 0,
x_diff, xtranslated,
svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = [{
labelName: "Financial Data",
no: 27,
yes: 10
}, {
labelName: "Production & completion data",
no: 24,
yes: 32
}, {
labelName: "Support Center Data",
no: 18,
yes: 21
}];
/*var drag = d3.drag()
.on('start.interrupt', function () {
xDrag.interrupt();
xDrag1.interrupt();
})
.on('start drag', function () {
xDrag.attr("transform", "translate(" + (d3.event.x) + "," + 0 +")")
xDrag1.attr("transform", "translate(" + (d3.event.x) + "," + height +")")
});*/
var drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault;
}
function dragged(d) {
//console.log("dragging");
//x_diff = start_x_old - start_x_new;
xDrag.attr("transform", "translate(" + [(d.x = d3.event.x), 0] + ")");
xDrag1.attr("transform", "translate(" + [(d.x = d3.event.x), height] + ")");
}
function dragended(d) {
xDrag.datum({
x: d3.event.x
});
xDrag1.datum({
x: d3.event.x
});
}
data.sort(function(a, b) {
return (b.yes + b.no) - (a.yes + a.no);
});
var x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(['#42c8c5', '#d05a59']);
var stack = d3.stack();
data.sort(function(a, b) {
return (b.yes + b.no) - (a.yes + a.no);
});
x.domain(data.map(function(d) {
return d.labelName;
}));
y.domain([0, d3.max(data, function(d) {
return (d.yes + d.no);
})]).nice();
z.domain(Object.keys(data[0]).slice(1));
g.selectAll(".serie")
.data(stack.keys(Object.keys(data[0]).slice(1))(data))
.enter().append("g")
.attr("class", "serie")
.attr("fill", function(d) {
return z(d.key);
})
.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("x", function(d) {
return x(d.data.labelName);
})
.attr("y", function(d) {
return y(d[1]);
})
.attr("height", function(d) {
return y(d[0]) - y(d[1]);
})
.attr("width", x.bandwidth());
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("transform", "rotate(-60)");
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks(10).pop()))
.attr("dy", "0.35em")
.attr("text-anchor", "start")
.attr("fill", "#000");
var xDrag = svg.selectAll(".serie")
.datum({
x: 0
})
.call(drag);
var xDrag1 = svg.selectAll(".axis--x")
.datum({
x: 0
})
.call(drag);
.bar {
fill: steelblue;
}
.axis path {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="700" height="500"></svg>
I used
d3.select(".graph")
.call(d3.behavior.zoom().on("zoom", redraw)).on("dblclick.zoom", null).on("wheel.zoom", null);
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
g.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
to create pan, but when i create an .on event listener and call redraw() with anything other than the way i have it, it comes back with nothing for d3.event.translate and d3.event.scale, so i can't update the transform. And i've seen code out there for zooming with a button for maps, but not a graph. It must be possible some how, but i don't see how. The code i have so far is...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
.node {
stroke: #000;
stroke-width: 0px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
.graphmap {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="graph"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// start draw zoom buttons
var zoom = d3.select(".zoom").append("svg")
.attr("width", 40)
.attr("height", 40);
// start zoom behavior
var mapZoom = d3.behavior.zoom()
.on("zoom", redraw);
function zoomButton(zoomDirection) {
if (zoomDirection == "in") {
var newZoom = mapZoom.scale() * 1.5;
var newX =
((mapZoom.translate()[0] - (width / 2)) * 1.5) + width / 2;
var newY =
((mapZoom.translate()[1] - (height / 2)) * 1.5) + height / 2;
}
else if (zoomDirection == "out") {
var newZoom = mapZoom.scale() * .75;
var newX = ((mapZoom.translate()[0] - (width / 2)) * .75) + width / 2;
var newY = ((mapZoom.translate()[1] - (height / 2)) * .75) + height / 2;
}
mapZoom.scale(newZoom).translate([newX,newY])
redraw();
}
function zoomed() {
projection.translate(mapZoom.translate()).scale(mapZoom.scale());
d3.selectAll("path.graticule").attr("d", geoPath);
d3.selectAll("path.countries").attr("d", geoPath);
d3.selectAll("circle.cities")
.attr("cx", function(d) {return projection([d.x,d.y])[0]})
.attr("cy", function(d) {return projection([d.x,d.y])[1]});
}
d3.select(".zoomin").on("click", function (){
zoomButton("in");
console.log(d3.behavior.zoom().event(d3.select(".zoomin")))
});
d3.select(".graph")
.call(d3.behavior.zoom().on("zoom", redraw)).on("dblclick.zoom", null).on("wheel.zoom", null);
d3.select(".zoomin")
.call(d3.behavior.zoom().on("zoom", redraw)).on("dblclick.zoom", redraw);
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
g.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
// start svg
var width = 1100,
height = 900;
var color = d3.scale.category20();
var force = d3.layout.force()
.gravity(.05)
.charge(-700)
.linkDistance(150)
.size([width, height]);
var svg = d3.select(".graph").append("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "graphmap");
zoomin = svg.append("g")
.attr("class", "zoomin");
zoomin.append("rect")
.attr("x", 10)
.attr("y", 10)
.attr("width", 30)
.attr("height", 30)
.attr("rx", 4)
.attr("ry", 4)
.attr("fill", "#dadae6");
var g = svg.append('g');
d3.json("miserables.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = g.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = g.selectAll("g")
.data(graph.nodes)
.enter().append("g")
.attr("class","node")
.call(force.drag);
node.append("circle")
.attr("r", function(d) { return Math.sqrt(d.group * 20); })
.style("fill", function(d) { return color(d.group); })
.attr("pointer-events", "auto")
.attr("class", "circlenode");
node.append("text")
.attr("text-anchor", "right")
.attr("fill","black")
.style("pointer-events", "none")
.attr("font-size", function(d) { 20 + 'px'; })
.attr("font-weight", function(d) { return "bold"; })
.text( function(d) { return d.name + ' (' + d.group + ')';});
setTimeout(function() {
node.classed("fixed", function(d) { return d.fixed = true; });
}, 9000);
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")";});
});
});
function dump(obj) {
var out = '';
for (var i in obj) {
out += i + ": " + obj[i] + "\n";
}
// or, if you wanted to avoid alerts...
var pre = document.createElement('pre');
pre.innerHTML = out;
document.body.appendChild(pre)
}
</script>
</body>
</html>
You'll see i was playing around with making a zoomButton function but i don't know how to see how to set it up to make it work. I've seen some demos out there of different ideas with the zoom functionality but i don't really understand how they work and what the functions are for. And the d3 documentation doesn't seem to provide much insight. And i haven't found any tutorials that go over what each of the functions do and how to handle events. Any help an explanation of how the zoom functions actually work would be appreciated.
It turns out to be very simple.
var zoomfactor = 1;
var zoomlistener = d3.behavior.zoom()
.on("zoom", redraw);
d3.select(".zoomin").on("click", function (){
zoomfactor = zoomfactor + 0.2;
zoomlistener.scale(zoomfactor).event(d3.select(".graph"));
});
d3.select(".zoomout").on("click", function (){
zoomfactor = zoomfactor - 0.2;
zoomlistener.scale(zoomfactor).event(d3.select(".graph"));
});
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
g.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
scale() sets the amount you want to zoom and event() calls the portion of the page you want to update.
I have seen this question: Is there a way to zoom into a D3 force layout graph?
But I got some unexpected behaivor from my graph - after few drags or zoom or pan all nodes just freezes and drag stop working.
I created this fiddle: http://jsfiddle.net/7gpweae9/9/
SO asked for code, so here is main part:
var svg = d3.select("#graph")
.append("svg:svg")
.attr("width", width)
.attr("height", height)
.attr("pointer-event", "all")
.append("svg:g")
.call(d3.behavior.zoom().on("zoom", zoom))
.append("svg:g");
svg.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr('fill', 'white');
var link = svg.selectAll(".link");
var node = svg.selectAll(".node");
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width,height])
.linkDistance(100)
.charge(-400)
.start();
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.call(drag);
node.append("circle")
.attr("class", "node-circle")
.attr("r", 12);
node.append("text")
.attr("x", 12)
.attr("y", ".35em")
.text(function(d) { return d.word; });
link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link");
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
function zoom() {
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("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
Perhaps I missed something, I never used d3 before.
UPD: It seems that freezing occurs after a certain period of time.
I replaced d3.layout.force() to force.drag() and now it works almost fine.
var nodes;
var links;
prepareData();
var graph = document.querySelectorAll("#graph")[0];
var height = 500;
var width = 500;
var svg = d3.select("#graph").append("svg:svg")
.attr("width", width)
.attr("height", height)
.attr("pointer-event", "all")
.append("svg:g")
.call(d3.behavior.zoom().on("zoom", zoom))
.append("svg:g");
svg.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr('fill', 'white');
var link = svg.selectAll(".link");
var node = svg.selectAll(".node");
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width,height])
.linkDistance(100)
.charge(-400)
.start();
var drag = force.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.call(drag);
node.append("circle")
.attr("class", "node-circle")
.attr("r", 12);
node.append("text")
.attr("x", 12)
.attr("y", ".35em")
.text(function(d) { return d.word; });
link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link");
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
}
function dragged(d) {
d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
}
function prepareData() {
nodes = [{"index":0,"word":"edit"},{"index":1,"word":"course","sentences":[29859]},{"index":2,"word":"needs","sentences":[29859]},{"index":3,"word":"fit","sentences":[29859]},{"index":4,"word":"slides","sentences":[29859]},{"index":5,"word":"print","sentences":[29859]},{"index":6,"word":"can","sentences":[29859]}];
links = [{"source":0,"target":1},{"source":0,"target":2},{"source":0,"target":3},{"source":0,"target":4},{"source":0,"target":5},{"source":0,"target":6}]
}
svg {
border: 1px solid black;
}
.link {
stroke: #000;
stroke-width: 1px;
}
.node-circle {
cursor: move;
fill: #ccc;
stroke: #000;
stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
<div id="graph"></div>
</body>
You had to make your drag variable a function and change d3.behaviour.drag to force.drag :
function drag(){
return force.drag()
// .origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
}
Ive updated your fiddle
Working JSFIDDLE : http://jsfiddle.net/7gpweae9/15/
First, I'm quite new in D3.
I'm trying to implement different behaviors in a single D3 graph, using these examples :
Drag + Zoom
Force-directed Graph
But my graph freezes after few seconds and I don't understand why...
Here is my code : http://jsfiddle.net/blt909/aVhd8/20/ [WORKING FINE VERSION]
var margin = {top: -5, right: -5, bottom: -5, left: -5};
var width = 500 - margin.left - margin.right,
height = 400- margin.top - margin.bottom;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([width + margin.left + margin.right, height + margin.top + margin.bottom]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select("#map").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
.call(zoom);
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
var container = svg.append("g");
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var tooltip = d3.select("body")
.append("foreignObject")
.append("xhtml:div")
.attr("id", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("color", "#eeeeee")
.style("visibility", "hidden")
.text("");
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.call(drag);
node.append("circle")
.attr("r", function(d) { return d.weight * 2+ 12; })
.style("fill", function(d) { return color(1/d.rating); });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
var linkedByIndex = {};
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index];
}
node.on("mouseover", function(d){
node.classed("node-active", function(o) {
thisOpacity = isConnected(d, o) ? true : false;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.classed("link-active", function(o) {
return o.source === d || o.target === d ? true : false;
});
d3.select(this).classed("node-active", true);
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", (d.weight * 2+ 12)*1.5);
})
.on("mouseout", function(d){
node.classed("node-active", false);
link.classed("link-active", false);
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", d.weight * 2+ 12);
});
function dottype(d) {
d.x = +d.x;
d.y = +d.y;
return d;
}
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
//ADDING force.start(); HERE SOLVES THE ISSUE
force.start();
}
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);
}
Thanks for your help
Putting force.start(); inside dragstarted function stops freezing.
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
force.start();
};
Here is fiddle