Created
May 4, 2017 18:40
-
-
Save robpataki/001e55ae2913856df154ddf797639fb4 to your computer and use it in GitHub Desktop.
SVG Connector helps with drawing connections between coordinates using dots and lines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example: https://jsfiddle.net/robertp/zsrn3e5o/