Constrained handles with D3 in a Fiddle
HTML
<script src="http://www.redblobgames.com/articles/curved-paths/making-of.js"></script>
<h2>Constrained handles with D3 in a Fiddle</h2>
<p>In November of 2014 (about 4 months ago) Amit Patel of <a href="http://www.redblobgames.com/">Red Blob Games</a> posted a remarkable blog entry on constrained motion <a href="http://www.redblobgames.com/articles/curved-paths/making-of.html">Making of: draggable handles</a>. I was really impressed with his approach. In that article, Amit develops a chainable constraint system within an interactive web page, all built around <a href="http://d3js.org/">D3</a>. This fiddle attempts to recreate some of those experiments in an <i>even more</i> interactive and mutable place, a <a href="http://jsfiddle.net/javajosh/6f1ec2vq/">fiddle</a>. His examples look something like this:</p>
<div id="example1"></div>
<h2>Questions</h2>
<ul>
<li><strike>Where does the drop shadow on the handle come from?</strike> <i>It comes from a defs tag in another svg component that is referred to by <code>filter: url(#drop-shadow)</code> in the css. Here's an <a href="http://www.gotoquiz.com/web-coding/web-design/recreating-a-logo-in-svg-from-a-png/">SVG tutorial that uses it.</a></i></li>
<li><strike>Since the SVG elt is added by "make_diagram" where do the lines come from?</strike> <i>Amit adds them manually after calling 'make_diagram'</i></li>
<li>Can the lines be inferred from the constraints?</li>
<li>What is a good place/method to add a listener to model mutations?</li>
</ul>
<div id="example2"></div>
<!-- This strange construction is used by the filter: url(#drop-shadow); statement! -->
<svg width="0" height="0">
<defs>
<filter id="drop-shadow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur in="SourceAlpha" stdDeviation="0.5"/>
<feOffset dx="0.5" dy="1" result="offsetblur"/>
<feFlood flood-color="#000000"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
...
CSS
body{
font: 14px sans-serif;
line-height: 20px;
margin: 20px;
}
svg {
background-color: #eee;
box-shadow: 0 1px 4px -2px #000;
overflow: hidden;
}
.draggable .circle {
fill: hsl(0,50%,50%);
box-shadow: 0 1px 4px -2px #000;
}
.draggable.dragging .circle, .draggable:hover .circle {
fill: hsl(0,60%,50%);
filter: url(#drop-shadow);
box-shadow: 0 1px 4px -2px #000;
}
li i {font-size: smaller}
JavaScript
//Okay, let's try the first example.
var obj1 = {x: 0};
var model1 = cartesian(
add(clamped(ref(obj1, 'x'), 0, 200), 100), constant(50));
make_diagram('#example1', model1, []);
//add a line
d3.select("#example1 svg").insert('line', ":first-child").attr('x1', 100).attr('y1', 50).attr('x2', 300).attr('y2', 50).attr('stroke', "#ddd").attr('stroke-width', "3px");
//Here's another example extracted from Amit's page.
var obj2 = {x: 3, y: 3};
var model2 = cartesian(Model.ref(obj2, 'x')
.clamped(0, 18)
.rounded()
.multiply(30)
.add(30),
Model.ref(obj2, 'y')
.clamped(0, 3)
.rounded()
.multiply(24)
.add(15));
make_diagram('#example2', model2, []);
//Make the dots manually.
for (var x = 0; x<=18; x++) {
for (var y = 0; y <= 3; y++) {
d3.select("#example2 svg")
.insert('circle', ":first-child")
.attr('cx', x*30+30)
.attr('cy', y*24+15)
.attr('r', 5)
.attr('fill', "#ddd");
}
}