var graph ={
"nodes": [
{"x": 469, "y": 410, move: true},
{"x": 493, "y": 364},
{"x": 442, "y": 365},
{"x": 467, "y": 314},
{"x": 477, "y": 248, move: true},
{"x": 425, "y": 207},
{"x": 402, "y": 155},
{"x": 369, "y": 196},
{"x": 350, "y": 148},
{"x": 539, "y": 222},
{"x": 594, "y": 235},
{"x": 582, "y": 185},
{"x": 633, "y": 200, move: true}
],
"links": [
{"source": 0, "target": 1},
{"source": 1, "target": 2},
{"source": 2, "target": 0},
{"source": 1, "target": 3},
{"source": 3, "target": 2},
{"source": 3, "target": 4},
{"source": 4, "target": 5},
{"source": 5, "target": 6},
{"source": 5, "target": 7},
{"source": 6, "target": 7},
{"source": 6, "target": 8},
{"source": 7, "target": 8},
{"source": 9, "target": 4},
{"source": 9, "target": 11},
{"source": 9, "target": 10},
{"source": 10, "target": 11},
{"source": 11, "target": 12},
{"source": 12, "target": 10}
]
}
var width = 500,
height = 190,
steps = function(){return +d3.select("#steps-selector").property("value")};
var inputDiv = d3.select("#input-div"),
tooltip = (function tooTip() {
var tt = d3.select("body").append("div")
.attr("id", "tool-tip")
.style({
position: "absolute",
color: "black",
background: "rgba(0,0,0,0)",
display: "none"
});
return function(message) {
return message ?
function() {
var rect = this.getBoundingClientRect();
tt
.style({
top: (rect.bottom + 6) + "px",
left: (rect.right + rect.left) / 2 + "px",
width: "10px",
padding: "0 1em 0 1em",
background: "#ccc",
'border-radius': "2px",
display: "inline-block"
})
.text(message)
}:
function() {
tt
.style({
display: "none"
})
}
}
})(),
easeings = ["linear", "quad", "cubic", "sin", "exp", "circle", "elastic", "back", "bounce"],
xEase = d3.ui.select({
base: d3.select("#input-div"),
oninput: positionnodes,
data: easeings,
initial: "bounce",
onmouseover: tooltip("x"),
onmouseout: tooltip()
}),
yEase = d3.ui.select({
base: d3.select("#input-div"),
oninput: positionnodes,
data: easeings,
initial: "circle",
onmouseover: tooltip("y"),
onmouseout: tooltip()
}),
t = (function(){
var s = d3.select("#input-div").selectAll(".time")
.data([{name: "tx", value: 0.75}, {name: "ty", value: 1.6}])
.enter().append("input")
.attr({
id: function(d){return d.name + "-selector"},
type: "number",
name: function(d){return d.name},
value: function(d){return d.value},
min: "0.1", max: "5", step: 0.5
})
.on("change", positionnodes)
.each(function(d){
d3.select(this).on("mouseover", tooltip(d.name))
})
.on("mouseout", tooltip());
return function(){
var values = [];
s.each(function(){
values.push(d3.select(this).property("value") * 1000);
});
return values;
}
})();
var force = d3.layout.force()
.size([width, height])
.charge(-100)
.linkDistance(6)
.on("tick", tick);
var drag = force.drag()
.on("dragstart", dragstart);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll(".link"),
node = svg.selectAll(".node");
//d3.json("graph.json", function(error, graph) {
// if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
link = link.data(graph.links)
.enter().append("line")
.attr("class", "link");
node = node.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 6)
.on("dblclick", dblclick)
.call(drag);
//});
function tick() {
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; });
force.alpha(0.1)
}
function dblclick(d) {
d3.select(this).classed("fixed", d.move = false);
}
function dragstart(d) {
d3.select(this).classed("fixed", d.move = true);
}
function positionnodes(){
var ns = "CB:emit/drag/transition/or-whatever-you-feel-like",
transitions = d3.select("body").selectAll("transitions")
.data([graph.nodes.filter(function(d){return d.move})]),
transitionsEnter = transitions.enter().append(function(){
return document.createElementNS(ns, "transitions")
}),
shadowNodes = transitions.selectAll("emitDrag")
.data(function(d){return d}),
shadowedData = [];
shadowNodes.enter().append(function(){
return document.createElementNS(ns, "emitDrag")
});
shadowNodes.each(function(d, i){
var n = d3.select(this);
shadowedData[i] = d;
dragstart.call(node.filter(function(s){return s === d;}).node(), d),
endAll = d3.cbTransition.endAll();
n.attr({cx: d.x, cy: d.y});
Object.defineProperties(d, {
px: {
get: function() {return d.x = +n.attr("cx")},
configurable: true
},
py: {
get: function() {return d.y = +n.attr("cy")},
configurable: true
}
});
});
force.start();
d3.range(steps()).reduce(function(o){
return (o.transition("cx").duration(t()[0]).ease(xEase.value())
.attr({
cx: function(d){
// return d.x + (Math.random() - 0.5) * width/5
return (1+3*Math.random())*width*0.2
}
}))
},shadowNodes)
.call(cleanUp, "px", "cx");
d3.range(steps()).reduce(function(o){
return (o.transition("cy").duration(t()[1]).ease(yEase.value())
.attr({
cy: function(d){
// return d.y + (Math.random() - 0.5) * height/5
return (1+3*Math.random())*height*0.2
}
}))
},shadowNodes)
.call(cleanUp, "py", "cy");
function cleanUp(selection, getter, attribute){
selection.each("end.each", function(d, i){
var n = d3.select(this);
Object.defineProperty(shadowedData[i], getter, {
value: +n.attr(attribute), writable: true
});
})
.call(endAll, function(){
transitions.remove();
}, "move-node");
}
}
positionnodes()
body {
margin: 0;
position: relative;
}
.link {
stroke: #000;
stroke-width: 1.5px;
}
.node {
cursor: move;
fill: #ccc;
stroke: #000;
stroke-width: 1.5px;
}
.node.fixed {
fill: #f00;
}
button, input {display: inline-block}
.input-div {
position: absolute;
top: 0;
left: 0;
/*white-space: pre;*/
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://rawgit.com/cool-Blue/d3-lib/master/transitions/end-all/1.0.0/endAll.js" charset="UTF-8"></script>
<script src="https://rawgit.com/cool-Blue/d3-lib/master/inputs/select/select.js" charset="UTF-8"></script>
<div id="input-div">
<button onclick = 'positionnodes()'> select the nodes to include then click me</button>
steps <input id="steps-selector" onchange = 'positionnodes()' type="number" name="steps" value = 10 min="1" max="100"/>
</div>