I have created a d3 force layout, and it works very well. Now I will add a group of data to my graph. I hope I could control the center of my new nodes. For example, supposed the center is (100,100), I hope the new nodes lay out into rectangle area like [(50,50) to (150,150)] as a whole.
var width = 500,
height = 500;
var nodes = [{id:0, n:'Tom'}, {id:1, n:'Join'}, {id:2, n:'John'}, {id:3, n:'Bob'}, {id:4, n:'4'}, {id:5, n:'5'}, {id:6, n:'6'}];
var links = [{source:0,target:1},{source:0,target:2},{source:0,target:3},{source:0,target:4},{source:0,target:5},{source:1,target:5},{source:1,target:6}];
// init force
var force = d3.layout.force()
.charge(-120)
.linkDistance(120)
.size([width, height]);
// init svg
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// set tick function
force.on("tick", function () {
d3.selectAll(".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;
});
// controll the coordinates here
d3.selectAll(".node").attr("transform", function(d){
if(d.flag == 1){
d.x = Math.max(50, Math.min(150, d.x));
d.y = Math.max(50, Math.min(150, d.y));
}
return "translate("+d.x+","+d.y+")";
});
}).on('end', function(){
svg.selectAll(".node").each(function(d){d.fixed=true;});
});
function setData(ns, ls){
var update = svg.selectAll(".link").data(ls);
update.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
update.exit().remove();
update = svg.selectAll(".node").data(ns);
update.enter().append("g")
.attr("class", "node")
.attr("id", function(d){return d.id})
.call(force.drag)
.call(function(p){
p.append("image")
.attr("class", "nodeimage")
.attr("width", "30px")
.attr("height", "30px")
.attr("x", "-15px")
.attr("y", "-15px");
p.append("text")
.attr("class", "nodetext")
.attr("dx", "-10px")
.attr("dy", "20px")
.style("font-size", "15px")
.text(function(d){return d.n});
});
update.exit().remove();
update.selectAll(".nodeimage")
.each(function() {
d3.select(this).datum(d3.select(this.parentNode).datum());
})
.attr("xlink:href", function(d){
var img;
if(d.flag == 1){
img = "http://www.gravatar.com/avatar/1eccef322f0beef11e0e47ed7963189b/?default=&s=80"
}else{
img = "http://www.gravatar.com/avatar/a1338368fe0b4f3d301398a79c171987/?default=&s=80";
}
return img;
});
force.nodes(ns)
.links(ls)
.start();
}
//init
setData(nodes, links);
setTimeout(function(){
//generate new data and merge to old data
nodes = nodes.concat(generateNewData());
setData(nodes, links);
//how do i control the coordinate of new nodes?
}, 3000);
function generateNewData(){
var ns = [];
for(var i = 0; i < 10; i++){
ns.push({id:i+100,n:'n'+i,flag:1});
}
return ns;
}
Here is my demo of jsfiddle:http://jsfiddle.net/cs4xhs7s/4/
The latest demo shows that the nodes can display in the rectangle, however, their coordinates are the same. I hope it is an available force layout.
https://jsfiddle.net/wpnq15mf/1/
var width = 500,
height = 500;
var nodes = [{id:0, n:'Tom'}, {id:1, n:'Join'}, {id:2, n:'John'}, {id:3, n:'Bob'}, {id:4, n:'4'}, {id:5, n:'5'}, {id:6, n:'6'}];
var links = [{source:0,target:1},{source:0,target:2},{source:0,target:3},{source:0,target:4},{source:0,target:5},{source:1,target:5},{source:1,target:6}];
// init force
var force = d3.layout.force()
.charge(-500)
.linkDistance(120)
.gravity(0.1)
.size([width, height]);
// init svg
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// set tick function
force.on("tick", function () {
d3.selectAll(".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;
});
// controll the coordinates here
d3.selectAll(".node").attr("transform", function(d){
if(d.flag == 1){
d.x = Math.max(50, Math.min(150, d.x));
d.y = Math.max(50, Math.min(150, d.y));
}
return "translate("+d.x+","+d.y+")";
});
}).on('end', function(){
svg.selectAll(".node").each(function(d){d.fixed=true;});
});
function setData(ns, ls){
var update = svg.selectAll(".link").data(ls);
update.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
update.exit().remove();
update = svg.selectAll(".node").data(ns);
update.enter().append("g")
.attr("class", "node")
.attr("id", function(d){return d.id})
.call(force.drag)
.call(function(p){
p.append("image")
.attr("class", "nodeimage")
.attr("width", "30px")
.attr("height", "30px")
.attr("x", "-15px")
.attr("y", "-15px");
p.append("text")
.attr("class", "nodetext")
.attr("dx", "-10px")
.attr("dy", "20px")
.style("font-size", "15px")
.text(function(d){return d.n});
});
update.exit().remove();
update.selectAll(".nodeimage")
.each(function() {
d3.select(this).datum(d3.select(this.parentNode).datum());
})
.attr("xlink:href", function(d){
var img;
if(d.flagx == 1){
img = "http://www.gravatar.com/avatar/1eccef322f0beef11e0e47ed7963189b/?default=&s=80"
}else{
img = "http://www.gravatar.com/avatar/a1338368fe0b4f3d301398a79c171987/?default=&s=80";
}
return img;
});
force.nodes(ns)
.links(ls)
.start();
}
//init
setData(nodes, links);
setTimeout(function(){
//generate new data and merge to old data
nodes = nodes.concat(generateNewData());
links = links.concat(generateNewLinks());
setData(nodes, links);
//how do i control the coordinate of new nodes?
}, 3000);
function generateNewData(){
var ns = [];
ns.push({id:6,n:'n'+i,flag:1, flagx:1});
for(var i = 1; i < 10; i++){
ns.push({id:i+6,n:'n'+i, flagx:1});
}
return ns;
}
function generateNewLinks(){
var ns = [];
ns.push({source:7,target:8});
ns.push({source:7,target:9});
ns.push({source:7,target:10});
ns.push({source:7,target:11});
ns.push({source:7,target:12});
ns.push({source:7,target:13});
ns.push({source:7,target:14});
ns.push({source:7,target:15});
ns.push({source:7,target:16});
return ns;
}
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Related
This is currently the code looks like however I seem to be unable to print the metric/weight on the network links or the network title on the nodes. If someone could help out that would be great. if you'd like to see the network diagram please send it over a message. I'm using D3.js version 3.
Below is my code:
var width = 1000, height = 700;
var color = d3.scale.category10();
var svg = d3.select("#d3-example").select("svg")
if (svg.empty()) {
svg = d3.select("#d3-example").append("svg")
.attr("width", width)
.attr("height", height);
}
d3.json("graph.json", function(error, graph) {
links = graph.links
var nodes = {};
links.forEach(function(link) {
link.source = nodes[link.source] ||
(nodes[link.source] = {name: link.source});
link.target = nodes[link.target] ||
(nodes[link.target] = {name: link.target});
});
var force = d3.layout.force()
.size([width, height])
.nodes(d3.values(nodes))
.links(links)
.gravity(0.1)
.linkDistance(200)
.on('tick', tick)
.charge(-1000)
.start();
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
link.append("text")
.attr("class", "data text-tooltip1")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 12px Arial")
.attr("transform", function(d) {
return "translate(" +
((d.source.y + d.target.y)/2) + "," +
((d.source.x + d.target.x)/2) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
console.log(d.target.name);
return d.target.name;
});
var node = svg.selectAll('.node')
.data(force.nodes())
.enter().append('circle')
.attr('class', 'node')
.attr('r', width * 0.02)
node.append("svg:title")
.text(function(d) { return d.name });
function tick(e) {
node.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.call(force.drag);
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; });
};
})
Try the below modifications in your code. This should solve your problem:
var width = 1000, height = 700;
var color = d3.scale.category10();
var svg = d3.select("#d3-example").select("svg")
if (svg.empty()) {
svg = d3.select("#d3-example").append("svg")
.attr("width", width)
.attr("height", height);
}
d3.json("graph.json", function(error, graph) {
links = graph.links
var nodes = {};
links.forEach(function(link) {
link.source = nodes[link.source] ||
(nodes[link.source] = {name: link.source});
link.target = nodes[link.target] ||
(nodes[link.target] = {name: link.target});
});
var force = d3.layout.force()
.size([width, height])
.nodes(d3.values(nodes))
.links(links)
.gravity(0.1)
.linkDistance(200)
.on('tick', tick)
.charge(-1000)
.start();
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link').attr('id', function(d){return d.id});
d3.selectAll(".link").each(function(d) {
svg
.append('svg:text').attr("class", "LinkLabelText")
.style("font-size", 15)
.style("fill", "teal")
.attr("x", (this.getTotalLength() / 2))
.attr("y", "-20")
.append('textPath')
.attr("xlink:href", "#" + d.id)
.html(function() {
return d.target.name;
});
});
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
console.log(d.target.name);
return d.target.name;
});
var node = svg.selectAll('.node')
.data(force.nodes())
.enter().append('circle')
.attr('class', 'node')
.attr('r', width * 0.02)
node.append("svg:text")
.text(function(d) { return d.name });
function tick(e) {
node.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.call(force.drag);
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; });
};
})
I am using the below code for the iamges and mouseover text. And now i have added the code for double click on a node to fade out all but it is not working and also link color is not changing. Can anyone help on this
var width = 960,
height = 500
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(0.1)
.charge(-120)
.linkDistance(30)
.size([width, height]);
var voronoi = d3.geom.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.clipExtent([[0, 0], [width, height]]);
d3.json("miserables1.json", function(error, json) {
if (error) throw error;
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link", "fill:red; stroke:red;");
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("svg:image")
.attr("xlink:href", function(d) { return d.imagen })
.on('dblclick', connectedNodes);
var circle = node.append("circle")
.attr("r", 4.5);
var label = node.append("text")
.attr("dy", ".35em")
.text(function(d) { return d.name; });
var cell = node.append("path")
.attr("class", "cell");
force.on("tick", function() {
cell
.data(voronoi(json.nodes))
.attr("d", function(d) { return d.length ? "M" + d.join("L") : null; });
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; });
circle
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
label
.attr("x", function(d) { return d.x + 8; })
.attr("y", function(d) { return d.y; });
});
});
var toggle = 0;
var linkedByIndex = {};
for (i = 0; i < graph.nodes.length; i++) {
linkedByIndex[i + "," + i] = 1;
};
graph.links.forEach(function (d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function neighboring(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
function connectedNodes() {
if (toggle == 0) {
//Reduce the opacity of all but the neighbouring nodes
d = d3.select(this).node().__data__;
node.style("opacity", function (o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
});
link.style("opacity", function (o) {
return d.index==o.source.index | d.index==o.target.index ? 1 : 0.1;
});
toggle = 1;
} else {
node.style("opacity", 1);
link.style("opacity", 1);
toggle = 0;
}
}
Having an atlas force graph with setup as follow, I would like to zoom in and out on mouse wheel events from anywhere in the drawing area but nodes (circles) in order to allow dragging individual nodes.
var svg = graph.append("svg")
.attr("width", width)
.attr("height", height)
.attr("pointer-events", "all")
.call(d3.behavior.zoom().on("zoom", redraw))
.append('g');
var link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link")
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("svg:g")
.attr("class", "node")
.on("dblclick", dblclick)
.call(force.drag);
node.append("circle")
.attr("class", "circle");
function redraw() {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
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 + ")"; });
});
The issue I have with this code is that clicking on a node and dragging it drags the whole graph, whereas when removing the call(... redraw) part it would let me drag individual nodes.
Is there a way to mix both behaviors and either prevent zooming when pointer is inside a node, or have node event prevalent on global (svg) events?
<!DOCTYPE html>
<html>
<head>
<title>Fidlde</title>
<script type="text/javascript" src="d3-master/d3.v3.min.js"></script>
<style>
.circle {
fill: #F5F5F5;
stroke: #999999;
stroke-width: 3;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
.link {
stroke: #999999;
stroke-opacity: .6;
stroke-width: 3;
}
</style>
</head>
<body>
<div id="graph">Hello!</div>
<script>
// graph size
var width = 400;
var height = 400;
var nodes = [{name: 'A'}, {name: 'B'}, {name: 'C'}, {name: 'D'}];
var edges = [{source: 'A', target: 'B'}, {source: 'B', target: 'C'}, {source: 'C', target: 'A'}, {source: 'C', target: 'D'}];
var nodeMap = {};
nodes.forEach(function(x) { nodeMap[x.name] = x; });
var links = edges.map(function(x) {
return { source: nodeMap[x.source], target: nodeMap[x.target], value: 1 };
});
var graph = d3.select("#graph");
var svg = graph.append("svg")
.attr("width", width)
.attr("height", height)
.attr("pointer-events", "all")
.call(d3.behavior.zoom().on("zoom", redraw))
.append('g');
var force = d3.layout.force()
.gravity(.25)
.distance(140)
.charge(-3500)
.size([width, height]);
/* Issue was here, the following code addresses it.
Thanks to Lars and Cool Blue - see comments
var drag = force.drag()
.on("dragstart", dragstart);
*/
var stdDragStart = force.drag().on("dragstart.force");
force.drag()
.on("dragstart", function(d){
//prevent dragging on the nodes from dragging the canvas
d3.event.sourceEvent.stopPropagation();
stdDragStart.call(this, d);
});
force
.nodes(nodes)
.links(links)
.friction(0.8)
.start();
var link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("svg:g")
.attr("class", "node")
.on("dblclick", dblclick)
.call(force.drag);
node.append("circle")
.attr("class", "circle")
.attr("r", 10);
node.append("text")
.attr("dx", -4)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
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 + ")"; });
});
// redraw after zooming
function redraw() {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
function dblclick(d) {
d3.select(this).classed("fixed", d.fixed = false);
}
function dragstart(d) {
d3.select(this).classed("fixed", d.fixed = true);
}
</script>
</body>
</html>
In oder to address the remaining dragging node issue, I made the following changes in the code:
node.enter()
.append("svg:g")
.attr("pointer-events", "all")
.attr("id", function(d) { return '_'+d.name })
.attr("class", "node")
.on("click", nodeClick)
.on("dblclick", nodeDoubleClick)
.on("mouseover", nodeMouseOver)
.on("mouseout", nodeMouseOut)
.call(force.drag);
function nodeClick(d) {
// fix the current node to its position
d.fixed = true;
}
function nodeDoubleClick(d) {
// release the current node
d.fixed = false;
}
function nodeMouseOver(d) {
// move the current node to front - some nodes are overlapping each others
var sel = d3.select(this);
sel.moveToFront();
// stop the whole graph
force.stop();
}
function nodeMouseOut(d) {
// resume node motion
force.start();
}
I also removed the following dragstart function which remained from previous code and was probably called while zooming.
/* function dragstart(d) {
d3.select(this).classed("fixed", d.fixed = true);
}
*/
Everything is now properly working. Thank you all for your contributions.
try this snippet of code :) also works
var width=600;
var height=600;
var nodes=[{
"name":"n1"
},{
"name":"n2"
},{
"name":"n3"
},{
"name":"n4"
},{
"name":"n5"
}];
var links=[{"source":0,"target":1},
{"source":0,"target":2},
{"source":0,"target":3},
{"source":1,"target":4},
{"source":2,"target":4},
{"source":3,"target":2}];
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("transform","translate(200,200)");
svg.append("rect")
.attr("width",width)
.attr("height",height)
.attr("fill","none")
.attr("pointer-events","all")
.call(d3.behavior.zoom().on("zoom", redraw));;
var force=d3.layout.force().charge(-400).linkDistance(200);
force.nodes(nodes).links(links).start();
var link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link")
var node = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("class","circle")
.call(force.drag);
function redraw() {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
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 + ")"; });
});
Fiddle: http://jsfiddle.net/jzaun/SCb7T/
Code:
var width = 500,
height = 500;
var dotSize = 50;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-10020)
.linkDistance(dotSize)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var graph = {
"nodes":[
{"name":"Name","group":1},
{"name":"Name","group":2},
{"name":"Name","group":3},
{"name":"Name","group":4}
],
"links":[
{"source":1,"target":0,"value":1},
{"source":2,"target":0,"value":1},
{"source":3,"target":0,"value":1}
]
};
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", dotSize)
.style("fill", function(d) { return color(d.group); });
node.append("image")
.attr("xlink:href", "http://lorempixel.com/64/64/cats")
.attr("x", -32)
.attr("y", -32)
.attr("width", 64)
.attr("height", 64);
node.append("title")
.text(function(d) { return d.name; });
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; });
});
You can't append image to circle elements -- the SVG spec doesn't allow this. Instead, append the images directly and set transform on them in the tick function instead of cx/cy.
Complete example here.
I'm generating voronoi paths based on some points in forced layout. I'd like to randomly assign these paths 1 of 10 classes and then wrap some of these classes with a clipPath that I can then apply to another element.
Is it possible to wrap svg tags around elements using d3 as opposed to appending?
Or is it even possible to use multiple paths generated by d3 in a clipPath?
Thank you for your help,
w = $(window).width();
h = $(window).height();
function ranNum(){
return Math.floor((Math.random()*10)+1);
}
$('#grid').css('height', h);
var vertices = d3.range(50).map(function(d) { return {x: d.x, y: d.y}; });
//console.log(vertices);
links = [];
voronoiVertices = [];
var force = d3.layout.force()
.nodes(vertices)
.size([w, h])
.linkDistance(60)
.charge(-900)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
//path gradient
var defs = svg.append('defs')
var radialGradient = defs.append('radialGradient')
.attr('id', 'pathGrad')
.attr('cx', '50%')
.attr('cy', '50%')
.attr('r', '50%')
.attr('fx', '50%')
.attr('fy', '50%');
var stop1 = radialGradient.append('stop')
.attr('offset', '.2')
.attr('stop-color', '#a8a8a8');
var stop2 = radialGradient.append('stop')
.attr('offset', '1')
.attr('stop-color', '#0000000');
//path dropShadow
var filterShadow = defs.append('filter')
.attr('id', 'pathShadow')
.attr('height', '130%');
var gCir = svg.append('g')
.attr("class", "gCircle");
var gPath = svg.append('g')
.attr("class", "gPath");
var circle = svg.selectAll("circle");
var path = gPath.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("fill", "url(#pathGrad)");
//wraps path with random class after generation
$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
var clip = defs.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", '900px')
.attr("height", '900px');
var gClip = svg.append("svg:g")
.attr('clip-path', 'url(#clip)');
/*
var clip = gClip.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", "clip.jpg")
.attr("x", "0px")
.attr("y", "0px")
.attr("width", w)
.attr("height", h);
*/
var selectPath = d3.selectAll('.path-10');
console.log(selectPath);
function tick() {
voronoiVertices = vertices.map(function(t){return [t.x, t.y]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
path.attr("fill", "url(#pathGrad)")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
circle = circle.data(vertices)
circle.enter().append("circle")
.call(force.drag)
.attr("r", 0)
.attr('class', function(d) { return d.index; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.transition().duration(5000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
});
There is a strange mix of jQuery and D3 in this. Try to do the things in D3 when you work with it. For example I'd rather do this:
.attr("class", function(d){return 'path-'+Math.floor((Math.random()*10)+1))});
than this:
$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
d3 has an exotic but smart way of doing things, it's better to learn this update pattern well before doing something serious.
And here is the working code:
w = 1200;
h = 500;
function ranNum(){
return Math.floor((Math.random()*10)+1);
}
var vertices = d3.range(50).map(function(d) { return {x: d.x, y: d.y}; });
//console.log(vertices);
links = [];
voronoiVertices = [];
var force = d3.layout.force()
.nodes(vertices)
.size([w, h])
.linkDistance(60)
.charge(-900)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
//path gradient
var defs = svg.append('defs')
var radialGradient = defs.append('radialGradient')
.attr('id', 'pathGrad')
.attr('cx', '50%')
.attr('cy', '50%')
.attr('r', '50%')
.attr('fx', '50%')
.attr('fy', '50%');
var stop1 = radialGradient.append('stop')
.attr('offset', '.2')
.attr('stop-color', '#a8a8a8');
var stop2 = radialGradient.append('stop')
.attr('offset', '1')
.attr('stop-color', '#0000000');
//path dropShadow
var filterShadow = defs.append('filter')
.attr('id', 'pathShadow')
.attr('height', '130%');
var gCir = svg.append('g')
.attr("class", "gCircle");
var gPath = svg.append('g')
.attr("class", "gPath");
var circle = svg.selectAll("circle");
var path = gPath.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("fill", "url(#pathGrad)");
//wraps path with random class after generation
//$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
var clip = defs.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", '900px')
.attr("height", '900px');
var gClip = svg.append("svg:g")
.attr('clip-path', 'url(#clip)');
function tick() {
voronoiVertices = vertices.map(function(t){return [t.x, t.y]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
path.attr("fill", "url(#pathGrad)")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
circle = circle.data(vertices)
circle.enter().append("circle")
.call(force.drag)
.attr("r", 0)
.attr('class', function(d) { return d.index; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.transition().duration(5000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
Good luck!