Skip to content

Instantly share code, notes, and snippets.

@robpataki
Created May 4, 2017 18:40
Show Gist options
  • Save robpataki/001e55ae2913856df154ddf797639fb4 to your computer and use it in GitHub Desktop.
Save robpataki/001e55ae2913856df154ddf797639fb4 to your computer and use it in GitHub Desktop.
SVG Connector helps with drawing connections between coordinates using dots and lines
// Colour scheme: https://coolors.co/f61067-5e239d-00f0b5-6decaf-f4f4ed
var NS = 'http://www.w3.org/2000/svg';
var Victor = function(artboardElementSelector) {
if (typeof artboardElementSelector === 'string') {
this.setArtboard(artboardElementSelector);
}
this.connections = {
all: []
}
return this;
}
Victor.prototype.setArtboard = function (artboardElementSelector) {
this.$artboard = document.querySelectorAll(artboardElementSelector)[0];
};
Victor.prototype.createSVG = function(width, height, options) {
var svg = document.createElementNS(NS,'svg');
svg.setAttributeNS(NS,'xlink','http://www.w3.org/1999/xlink');
svg.setAttribute('width', width);
svg.setAttribute('height', height);
for (var r in options) {
if (r === 'style') {
for (var s in options[r]) {
svg.style[s] = options[r][s];
}
} else if (r === 'class') {
svg.setAttribute(r, options[r]);
} else {
svg.setAttributeNS(NS, r, options[r]);
}
}
this.$artboard.appendChild(svg);
return svg;
};
Victor.prototype.createShape = function(type) {
var shape = document.createElementNS(NS, type);
this.$artboard.appendChild(shape);
return shape;
};
Victor.prototype.createDot = function(options) {
var x = options.x,
y = options.y,
size = options.size,
radius = size * 0.5,
fill = options.fill || 'black';
x -= radius;
y -= radius;
var svg = this.createSVG(
size,
size, {
class: 'v-dot',
style: {
position: 'absolute',
left: x + 'px',
top: y + 'px'
}
}
);
var shape = this.createShape('circle');
shape.setAttributeNS(null, 'cx', radius);
shape.setAttributeNS(null, 'cy', radius);
shape.setAttributeNS(null, 'r', radius);
shape.setAttributeNS(null, 'fill', fill);
svg.appendChild(shape);
function updatePosition (newX, newY) {
x = newX - radius;
y = newY - radius;
svg.style.top = y + 'px';
svg.style.left = x + 'px';
};
function destroy () {
svg.removeChild(shape);
shape = undefined;
svg.parentNode.removeChild(svg);
svg = undefined;
};
return {
el: svg,
position: function() {
return {
x: x,
y: y
}
},
radius: radius,
size: size,
updatePosition: updatePosition,
destroy: destroy
};
};
Victor.prototype.getConnectionData = function(sourceCoords, targetCoords) {
var padding = 20;
var halfPadding = padding * 0.5;
var x1 = sourceCoords.x;
var y1 = sourceCoords.y;
var x2 = targetCoords.x;
var y2 = targetCoords.y;
var boxX = Math.min(x1, x2);
var boxY = Math.min(y1, y2);
var width = Math.abs(x1 - x2);
var height = Math.abs(y1 - y2);
var sx = Math.min(x1, x2);
var sy = sx === x1 ? y1 : y2;
var tx = sx === x1 ? x2 : x1;
var ty = tx === x1 ? y1 : y2;
// In case the 2 x values equal
ty = sx === tx ? y2 : ty;
return {
sx: 0 + halfPadding,
sy: sy < ty ? 0 + halfPadding : sy - ty + halfPadding,
tx: width + halfPadding,
ty: ty < sy ? 0 + halfPadding : ty - sy + halfPadding,
boxX: boxX - halfPadding,
boxY: boxY - halfPadding,
width: width + padding,
height: height + padding
};
};
Victor.prototype.connect = function(sourceCoords, targetCoords) {
var id = Math.round(Math.random() * 10000000 + Math.random() * 1000000);
var data = this.getConnectionData(sourceCoords, targetCoords);
var sx = data.sx,
sy = data.sy,
tx = data.tx,
ty = data.ty,
boxX = data.boxX,
boxY = data.boxY,
width = data.width,
height = data.height;
// Create a container for the drawing
var container = document.createElement('div');
container.setAttribute('class', 'v-connection');
this.$artboard.appendChild(container);
var sourceDot = this.createDot({
x: sourceCoords.x - boxX,
y: sourceCoords.y - boxY,
size: 12,
radius: 6,
fill: '#F61067'
});
container.appendChild(sourceDot.el);
var targetDot = this.createDot({
x: targetCoords.x - boxX,
y: targetCoords.y - boxY,
size: 12,
radius: 6,
fill: '#00F0B5'
});
container.appendChild(targetDot.el);
container.style.width = width + 'px';
container.style.height = height + 'px';
container.style.left = boxX + 'px';
container.style.top = boxY + 'px';
var svg = this.createSVG(
width, height, {
class: 'v-line',
style: {
position: 'absolute',
overflow: 'hidden'
}
}
);
container.appendChild(svg);
var shape = this.createShape('line');
shape.setAttributeNS(null, 'x1', sx);
shape.setAttributeNS(null, 'y1', sy);
shape.setAttributeNS(null, 'x2', tx);
shape.setAttributeNS(null, 'y2', ty);
shape.setAttributeNS(null, 'stroke-width', 2);
shape.setAttributeNS(null, 'stroke', '#5E239D');
svg.appendChild(shape);
/* Repainting the connection */
var _this = this;
function repaint(newSourceCoords, newTargetCoords) {
var data = _this.getConnectionData(newSourceCoords, newTargetCoords);
var sx = data.sx,
sy = data.sy,
tx = data.tx,
ty = data.ty,
boxX = data.boxX,
boxY = data.boxY,
width = data.width,
height = data.height;
// Update container
container.style.width = width + 'px';
container.style.height = height + 'px';
container.style.left = boxX + 'px';
container.style.top = boxY + 'px';
// Update the dots
sourceDot.updatePosition(newSourceCoords.x - boxX, newSourceCoords.y - boxY);
targetDot.updatePosition(newTargetCoords.x - boxX, newTargetCoords.y - boxY);
// Update the line
shape.setAttributeNS(null, 'x1', sx);
shape.setAttributeNS(null, 'y1', sy);
shape.setAttributeNS(null, 'x2', tx);
shape.setAttributeNS(null, 'y2', ty);
};
function show() {
container.style.display = 'block';
};
function hide() {
container.style.display = 'none';
};
function destroy() {
sourceDot.destroy();
targetDot.destroy();
_this.$artboard.removeChild(container);
var matchingItem = -1;
for (var i = 0; i < _this.connections.all.length; i ++) {
var collection = _this.connections.all[i];
if (collection.id === id) {
matchingItem = i;
break;
}
}
if (matchingItem >= 0) {
_this.connections.all.splice(matchingItem, 1);
}
};
var connection = {
id: id,
el: svg,
sourceDot: sourceDot,
targetDot: targetDot,
show: show,
hide: hide,
destroy: destroy,
repaint: repaint
};
this.connections.all.push(connection);
return connection;
};
@robpataki
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment