Skip to content

Instantly share code, notes, and snippets.

@jedwood
Created February 14, 2014 00:14
Show Gist options
  • Save jedwood/8986724 to your computer and use it in GitHub Desktop.
Save jedwood/8986724 to your computer and use it in GitHub Desktop.
Tiny helper for connecting two elements with SVG lines. Assumes jQuery (for now). Original by @johndilworth
<!DOCTYPE html>
<html>
<head>
<title>SVG Lines</title>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="belay.js"></script>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<style>
body{
background: #eee;
}
.grid{
padding: 30px;
}
.fact,.source, .child, .parent{
background: #fff;
margin: 10px 0px;
border-radius: 4px;
min-height: 30px;
padding: 10;
}
ol{
margin: 0;
padding: 0;
}
li{
list-style:none;
padding: 0;
}
h1,h2,h3{
margin-top:0;
}
li p{margin-bottom:0;}
</style>
</head>
<body>
<div class="grid">
<div class="row">
<h1>On Belay!</h1>
</div>
<div class="row">
<div class="col-sm-5">
<ol id="wives">
<li class="parent" id="parent-1">
<h3>Kendall</h3>
</li>
<li class="parent" id="parent-2">
<h3>Dave</h3>
</li>
<li class="parent" id="parent-3">
<h3>Tana</h3>
</li>
</ol>
</div>
<div class="col-sm-2"></div>
<div class="col-sm-5">
<ol id="children">
<li class="child parent-1">John</li>
<li class="child parent-1">Jed</li>
<li class="child parent-1">Josh</li>
<li class="child parent-1">Kory</li>
<li class="child parent-1">Bonnie</li>
<li class="child parent-2">Rob</li>
<li class="child parent-3">Freestone</li>
</ol>
</div>
</div>
</div>
</body>
<script>
function drawConnectors(){
//Draws a line from the wife to her children, based on each child having a class that is the id of the parent.
$(".parent").each(function(){
var theID = this.id;
$("."+theID).each(function(i,e){
var rand = Math.random() * .7 + .3;
Belay.set('animationDuration', rand)
Belay.on($("#"+theID), e)
});
})
}
// redraw if window is resized
$(window).resize(function(){
Belay.off();
drawConnectors();
});
Belay.init({strokeWidth: 1});
Belay.set('strokeColor', '#999');
drawConnectors();
</script>
</html>
var Belay = (function(){
var settings = {
strokeColor : '#fff',
strokeWidth : 2,
opacity : 1,
fill : 'none',
animate : true,
animationDirection: 'right',
animationDuration : .75
};
var me = {};
me.init = function(initObj) {
if (initObj) {
$.each(initObj, function(index, value) {
//TODO validation on settings
settings[index] = value;
});
}
}
me.set = function(prop, val){
//TODO validate
settings[prop] = val;
}
me.on = function(el1, el2){
var $el1 = $(el1);
var $el2 = $(el2);
if ($el1.length && $el2.length) {
var svgheight
,p
,svgleft
,svgtop
,svgwidth
var el1pos = $(el1).offset();
var el2pos = $(el2).offset();
var el1H = $(el1).outerHeight();
var el1W = $(el1).outerWidth();
var el2H = $(el2).outerHeight();
var el2W = $(el2).outerWidth();
svgleft = Math.round(el1pos.left + el1W);
svgwidth = Math.round(el2pos.left - svgleft);
var curvinessFactor, cpt;
////Determine which is higher/lower
if( (el2pos.top+(el2H/2)) <= ( el1pos.top+(el1H/2))){
// console.log("low to high");
svgheight = Math.round((el1pos.top+el1H/2) - (el2pos.top+el2H/2));
svgtop = Math.round(el2pos.top + el2H/2) - settings.strokeWidth;
cpt = Math.round(svgwidth*Math.min(svgheight/300, 1));
p = "M0,"+ (svgheight+settings.strokeWidth) +" C"+cpt+","+(svgheight+settings.strokeWidth)+" "+(svgwidth-cpt)+"," + settings.strokeWidth + " "+svgwidth+"," + settings.strokeWidth;
}else{
// console.log("high to low");
svgheight = Math.round((el2pos.top+el2H/2) - (el1pos.top+el1H/2));
svgtop = Math.round(el1pos.top + el1H/2) - settings.strokeWidth;
cpt = Math.round(svgwidth*Math.min(svgheight/300, 1));
p = "M0," + settings.strokeWidth + " C"+ cpt +",0 "+ (svgwidth-cpt) +","+(svgheight+settings.strokeWidth)+" "+svgwidth+","+(svgheight+settings.strokeWidth);
}
//ugly one-liner
$ropebag = $('#ropebag').length ? $('#ropebag') : $('body').append($( "<div id='ropebag' />" )).find('#ropebag');
var svgnode = document.createElementNS('http://www.w3.org/2000/svg','svg');
var newpath = document.createElementNS('http://www.w3.org/2000/svg',"path");
newpath.setAttributeNS(null, "d", p);
newpath.setAttributeNS(null, "stroke", settings.strokeColor);
newpath.setAttributeNS(null, "stroke-width", settings.strokeWidth);
newpath.setAttributeNS(null, "opacity", settings.opacity);
newpath.setAttributeNS(null, "fill", settings.fill);
svgnode.appendChild(newpath);
//for some reason, adding a min-height to the svg div makes the lines appear more correctly.
$(svgnode).css({left: svgleft, top: svgtop, position: 'absolute',width: svgwidth, height: svgheight + settings.strokeWidth*2, minHeight: '20px' });
$ropebag.append(svgnode);
if (settings.animate) {
// THANKS to http://jakearchibald.com/2013/animated-line-drawing-svg/
var pl = newpath.getTotalLength();
// Set up the starting positions
newpath.style.strokeDasharray = pl + ' ' + pl;
if (settings.animationDirection == 'right') {
newpath.style.strokeDashoffset = pl;
} else {
newpath.style.strokeDashoffset = -pl;
}
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
// WON'T WORK IN IE. If you want that, use requestAnimationFrame to update instead of CSS animation
newpath.getBoundingClientRect();
newpath.style.transition = newpath.style.WebkitTransition ='stroke-dashoffset ' + settings.animationDuration + 's ease-in-out';
// Go!
newpath.style.strokeDashoffset = '0';
}
}
}
me.off = function(){
$("#ropebag").empty();
}
return me;
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment