Created
May 12, 2017 11:41
-
-
Save robpataki/cedec554cf860b2fd96be6b2f87a3513 to your computer and use it in GitHub Desktop.
Betterer version of SVGConnector
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
/*! | |
* Dot Connector | |
* Rob Pataki [hello@robp.io] [https://robp.io] | |
* Date: 2017-05-04 | |
* Demo: https://jsfiddle.net/robertp/zsrn3e5o/ | |
*/ | |
var NS = 'http://www.w3.org/2000/svg'; | |
var DotCon = function(artboardElement) { | |
this.$artboard = artboardElement; | |
this.connections = { | |
all: [] | |
} | |
return this; | |
} | |
DotCon.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; | |
}; | |
DotCon.prototype.createShape = function(type) { | |
var shape = document.createElementNS(NS, type); | |
this.$artboard.appendChild(shape); | |
return shape; | |
}; | |
DotCon.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 | |
}; | |
}; | |
DotCon.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 | |
}; | |
}; | |
DotCon.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: '#C12026' | |
}); | |
container.appendChild(sourceDot.el); | |
var targetDot = this.createDot({ | |
x: targetCoords.x - boxX, | |
y: targetCoords.y - boxY, | |
size: 12, | |
radius: 6, | |
fill: '#1D9B48' | |
}); | |
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', '#C12026'); | |
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; | |
}; | |
DotCon.prototype.show = function() { | |
this.$artboard.style.display = 'block'; | |
}; | |
DotCon.prototype.hide = function() { | |
this.$artboard.style.display = 'none'; | |
}; | |
DotCon.prototype.destroy = function() { | |
console.log('[Dotcon] - destroy()'); | |
for (var i = 0; i < this.connections.all.length; i ++) { | |
var connection = this.connections.all[i]; | |
connection.destroy(); | |
} | |
this.connections.all = []; | |
}; | |
module.exports = DotCon; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example: https://jsfiddle.net/robertp/zsrn3e5o/