|
<!DOCTYPE html> |
|
|
|
<head> |
|
<meta charset="utf-8"> |
|
<script src="https://d3js.org/d3.v3.min.js"></script> |
|
<link href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" |
|
rel="stylesheet"> |
|
|
|
|
|
<style> |
|
body { |
|
margin: 0; |
|
position: fixed; |
|
top: 0; |
|
right: 0; |
|
bottom: 0; |
|
left: 0; |
|
} |
|
.node { |
|
stroke: #fff; |
|
stroke-width: 1.5px; |
|
} |
|
.node-active { |
|
stroke: #555; |
|
stroke-width: 1.5px; |
|
} |
|
.link { |
|
stroke: #555; |
|
stroke-opacity: .3; |
|
} |
|
.link-active { |
|
stroke: #a80000; |
|
stroke-opacity: 1; |
|
} |
|
.overlay { |
|
fill: none; |
|
pointer-events: all; |
|
} |
|
#anid { |
|
stroke: #c10000; |
|
stroke-width: 11.5px; |
|
} |
|
#map { |
|
border: 2px #333333 dashed; |
|
width: 600px; |
|
height: 600px; |
|
} |
|
button { |
|
position: absolute; |
|
width: 40px; |
|
height: 30px; |
|
} |
|
button#reset { |
|
margin-top: 30px; |
|
margin-left: 11px; |
|
} |
|
</style> |
|
|
|
|
|
</head> |
|
|
|
<body> |
|
<div id="map"> |
|
<button id='reset' title='Reset Layout to Beginning'> |
|
<i class='fa fa-undo'></i> |
|
</button> |
|
</div> |
|
|
|
<script> |
|
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 |
|
}, { |
|
"name": "28", |
|
"rating": 1, |
|
"id": 971 |
|
}, { |
|
"name": "29", |
|
"rating": 1, |
|
"id": 971 |
|
}], |
|
"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": 27, |
|
"target": 27, |
|
"value": 6, |
|
"label": "byitself" |
|
}, { |
|
"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": "maa" |
|
}, { |
|
"source": 20, |
|
"target": 28, |
|
"value": 3, |
|
"label": "maa" |
|
}, { |
|
"source": 13, |
|
"target": 21, |
|
"value": 3, |
|
"label": "maa" |
|
}, { |
|
"source": 11, |
|
"target": 19, |
|
"value": 3, |
|
"label": "maa" |
|
}] |
|
} |
|
|
|
|
|
var margin = { |
|
top: -5, |
|
right: -5, |
|
bottom: -5, |
|
left: -5 |
|
}; |
|
|
|
var width = 600 - margin.left - margin.right, |
|
height = 600 - margin.top - margin.bottom; |
|
|
|
var color = d3.scale.category20b(); |
|
|
|
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 timeOuts = []; |
|
|
|
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"); |
|
|
|
|
|
|
|
|
|
// initialize layout (for reset button) |
|
var initForce = function() { |
|
|
|
|
|
// clear svg |
|
container.selectAll('*').remove(); |
|
|
|
// counter |
|
var countern = 0; |
|
|
|
|
|
|
|
force |
|
.nodes(graph.nodes) |
|
.links(graph.links) |
|
.start(); |
|
|
|
graph.links.forEach(function(d, i) { |
|
timeOuts.push(setTimeout(function() { |
|
|
|
|
|
var nodes = graph.nodes.filter(function(n, i) { |
|
return d.source.index == i || d.target.index == i |
|
}); |
|
|
|
|
|
// add one to counter |
|
countern = countern + 1; |
|
console.log(countern); //just to check |
|
|
|
// add edge |
|
container.append("line") |
|
.datum(d) |
|
.attr("class", "link") |
|
.style("stroke-width", 6) |
|
.style("stroke","red") |
|
|
|
// delay on line appearing - do i need i * 10 ??? |
|
// delay is half delay of node. |
|
.transition() |
|
.delay(function(d, i) { |
|
return i * 10; |
|
}) //do i need this ? |
|
.duration(350) |
|
.style("stroke-width", function(d) { |
|
return Math.sqrt(d.value); |
|
}) |
|
.style("stroke","#555555") |
|
; |
|
|
|
nodes.forEach(function(node) { |
|
var nodeG = container.append("g") |
|
.datum(node) |
|
.attr("class", "node") |
|
.attr("cx", function(d) { |
|
return d.x; |
|
}) |
|
.attr("cy", function(d) { |
|
return d.y; |
|
}) |
|
.call(drag); |
|
|
|
|
|
|
|
// add node |
|
nodeG.append("circle") |
|
.style('opacity', 0) |
|
.attr("r", function(d) { |
|
return d.weight * 2 + 5; |
|
}) |
|
.style("fill", function(d) { |
|
return color(1 / d.rating); |
|
}) |
|
// delay on line appearing - do i need i * 10 ??? |
|
// delay is half delay of node. |
|
.transition() |
|
.delay(function(d, i) { |
|
return i * 10; |
|
}) |
|
.duration(700) |
|
.style('opacity', 1); |
|
|
|
|
|
|
|
}); |
|
force.resume(); |
|
}, 700 * i)); |
|
}); |
|
|
|
|
|
|
|
force.on("tick", function() { |
|
container.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; |
|
}) |
|
|
|
; |
|
|
|
container.selectAll(".node").attr("transform", function(d) { |
|
return "translate(" + d.x + "," + d.y + ")"; |
|
}); |
|
|
|
container.select("text").remove() // remove previous text |
|
|
|
// add counter |
|
container |
|
.append("text") |
|
.attr("x", 555 ) |
|
.attr("y", 60 ) |
|
.style("text-anchor", "middle") |
|
.text(countern) |
|
.attr("font-family", "sans-serif") |
|
.attr("font-size", "20px") |
|
.attr("fill", "#4d648d"); |
|
|
|
|
|
|
|
|
|
|
|
}); |
|
|
|
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]; |
|
} |
|
|
|
container.selectAll(".node").on("mouseover", function(d) { |
|
|
|
container.selectAll(".node").classed("node-active", function(o) { |
|
thisOpacity = isConnected(d, o) ? true : false; |
|
this.setAttribute('fill-opacity', thisOpacity); |
|
return thisOpacity; |
|
}); |
|
|
|
container.selectAll(".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(700) |
|
.attr("r", (d.weight * 2 + 12) * 1.5); |
|
}) |
|
|
|
.on("mouseout", function(d) { |
|
|
|
container.selectAll(".node").classed("node-active", false); |
|
container.selectAll(".link").classed("link-active", false); |
|
|
|
d3.select(this).select("circle").transition() |
|
.duration(700) |
|
.attr("r", d.weight * 2 + 12); |
|
}); |
|
|
|
|
|
}; //initforce end |
|
|
|
|
|
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); |
|
} |
|
|
|
// When the user clicks on the "Reset" button, we'll |
|
// start the whole process over again. |
|
|
|
|
|
d3.select('#reset').on('click', function() { |
|
|
|
|
|
// Re-initialize to start over again. |
|
timeOuts.forEach(function(timeOutFn) { |
|
clearTimeout(timeOutFn); |
|
|
|
|
|
}); |
|
initForce(); |
|
|
|
}); |
|
|
|
|
|
|
|
// Now we can initialize the force layout so that it's ready |
|
// to run. |
|
|
|
initForce(); |
|
</script> |
|
</body> |