Skip to content

Instantly share code, notes, and snippets.

@srajagop
Created August 6, 2015 20:16
Show Gist options
  • Save srajagop/e22a89826170ccde3e19 to your computer and use it in GitHub Desktop.
Save srajagop/e22a89826170ccde3e19 to your computer and use it in GitHub Desktop.
Feckless Spork
<div ng-app="app" ng-controller="AppController" ng-class="{'x-ray': xRay, 'horizontal': horiz}">
<h1>Proof of Concept</h1>
<p>Using only CSS & HTML to draw the diagram, aside from using Angular to build the HTML and hook on the magic calculations. <b>No canvas, no SVG.</b></p>
<p>Tested in Chrome, Safari, Firefox. And it seems to also work in IE 10+</p>
<p><label><input type="checkbox" ng-model="xRay" /> X-Ray Goggles</label></p>
<spork config="config" model="model"></spork>
</div>
// utility methods
var getNumbers = function(target) {
var numbers = {};
if (target) {
numbers = {
t: target.offsetTop,
r: target.offsetLeft + target.offsetWidth,
b: target.offsetTop + target.offsetHeight,
l: target.offsetLeft,
w: target.offsetWidth,
h: target.offsetHeight
};
// find x|y center
numbers.cx = (numbers.l + (numbers.w / 2));
numbers.cy = (numbers.t + (numbers.h / 2));
}
return numbers;
};
var getMetrics = function(el, parent, rtl, factor) {
rtl = rtl || false;
factor = factor || false;
var en = getNumbers(el);
var pn = getNumbers(parent);
var p1 = {
x: en.cx,
y: en.cy
};
var p2 = {
x: pn.cx,
y: pn.cy
};
// angle in radians
var rads = Math.atan2(p2.y - p1.y, p2.x - p1.x);
// angle in degrees
var degs = rads * 180 / Math.PI;
if (rtl) {
// this because if the line orientation is right to left
degs += 180;
}
// length of line between two points
// last operation as this alters the number set
var lens = Math.sqrt(((p1.x -= p2.x) * p1.x) + ((p1.y -= p2.y) * p1.y));
if (factor) {
// for if we want the line length to extend beyond the node
lens = lens * factor;
}
// returns object of calculations
return {
lens: lens,
rads: rads,
degs: degs
};
};
angular.module('app', [])
.controller('AppController', ['$scope', function($scope) {
// nodes data model
$scope.model = [{
id: 0,
children: [{
id: 1,
children: [{
id: 11,
children: [{
id: 111
}, {
id: 112
}]
}, {
id: 12
}]
},
{
id: 2,
children: [{
id: 21
}, {
id: 22
}, {
id: 23,
children: [{
id: 231
}, {
id: 232
}, {
id: 233
}]
}, {
id: 24
}]
},
]
}];
}])
.directive('spork', [function spork() {
return {
restrict: 'E',
replace: true,
scope: {
config: '=?',
model: '='
},
controller: ['$scope', function controller($scope) {
var me = this;
$scope.connector = function connector(el, parent) {
el = angular.element('#node-' + el)[0];
parent = angular.element('#node-' + parent)[0];
var metrics = getMetrics(el, parent, true);
return {
width: Math.round(metrics.lens) + 'px',
transform: 'translateY(-50%) rotate(' + Math.round(metrics.degs) + 'deg)'
};
};
}],
template: '' +
'<div class="spork">' +
'<ul>' +
'<li ng-repeat="a in model">' +
'<div id="node-{{a.id}}">' +
'<div class="node"><span class="label">{{a.id}}</span></div>' +
'</div>' +
'<ul ng-if="a.children.length">' +
'<li ng-repeat="b in a.children">' +
'<div id="node-{{b.id}}">' +
'<div class="node"><span class="label">{{b.id}}</span></div>' +
'<div class="line" ng-style="connector(b.id, a.id)"></div>' +
'</div>' +
'<ul ng-if="b.children.length">' +
'<li ng-repeat="c in b.children">' +
'<div id="node-{{c.id}}">' +
'<div class="node"><span class="label">{{c.id}}</span></div>' +
'<div class="line" ng-style="connector(c.id, b.id)"></div>' +
'</div>' +
'<ul ng-if="c.children.length">' +
'<li ng-repeat="d in c.children">' +
'<div id="node-{{d.id}}">' +
'<div class="node"><span class="label">{{d.id}}</span></div>' +
'<div class="line" ng-style="connector(d.id, c.id)"></div>' +
'</div>' +
'</li>' +
'</ul>' +
'</li>' +
'</ul>' +
'</li>' +
'</ul>' +
'</li>' +
'</ul>' +
'</div>'
};
}]);
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
//
.spork {
position: relative;
ul {
margin: 0;
padding: 0;
list-style: none;
flex: 1 0 auto;
display: flex;
flex-direction: column;
align-items: flex-start;
}
li {
flex: 0 0 auto;
margin: 0 .5em;
display: flex;
flex-direction: row;
align-items: center;
transition: .2s;
> div {
margin: .5em;
position: relative;
flex: 0 0 auto;
}
}
.node {
position: relative;
z-index: 2;
padding: 2em;
background: skyblue;
/* box-shadow: 0 0 0 .2em currentColor inset; */
border: 2px solid;
border-radius: 100%;
transition: .2s;
cursor: default;
}
.label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
.line {
position: absolute;
z-index: 1;
padding: .2em;
background: currentColor;
top: 50%;
right: 50%;
transform-origin: right center;
transition: .2s;
pointer-events: none;
}
}
// playground
.spork {
ul {
margin: .5em;
}
li {
margin: .5em;
border-radius: .3em;
li {
font-size: .85em;
}
&:hover {
> div {
.node,
.node+.line {
background: mix(#fff, yellowGreen, 5%);
}
.node {
z-index: 3;
transform: scale(1.2);
border-color: #fff;
&:hover {
transform: scale(2) !important;
box-shadow: 0 0 0 .3em fade-out(yellowGreen, .7);
}
+ .line {
padding: .4em;
}
}
}
}
}
}
// electrical wiring
.x-ray .spork li {
background: rgba(#000,.1);
.node {
opacity: .7;
}
}
// basics
body {
font-family: sans-serif;
font-size: 15px;
}
.spork *, .spork *::after, .spork *::before {
box-sizing: border-box;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment