Brief history of this code:
Hi, friends i am learning Raphael js and creating an application in it. i needed to create a mechanism which would allow more than one connections between two Raphael rectangles and i was having alot of i mean tons of issues and thoughts on how to do it and it kept me busy a lot. i was so tired that i had to make stackoverflow account and ask help about it, but the help i got added one more thought to me. At last i am able to create a mechanism which will not only allow more than one connection but also renders it on moving the rectangle. yes i know it still has issues but this is the basic code to help any other who need this and can use it and don't have struggle as much as i did.Lets get started:
create and HTML document and use the following code: <!doctype html>
<html>
<body>
<div id="canvas"></div>
<script type="text/javascript" src="js/Raphael.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
in the app.js use the following code:
function lineDistance( point1, point2 )
{
var xs = 0;
var ys = 0;
xs = point2.x - point1.x;
xs = xs * xs;
ys = point2.y - point1.y;
ys = ys * ys;
return Math.sqrt( xs + ys );
}
Raphael.fn.connection = function (objFrom, objTo, line, bg) {
if (objFrom.line && objFrom.from && objFrom.to) {
line = objFrom;
objFrom = line.from;
objTo = line.to;
}
var bb1 = objFrom.getBBox(),
bb2 = objTo.getBBox();
var obj1freeNodes = objFrom.data('freeNodes');
var obj2freeNodes = objTo.data('freeNodes');
var p = [];
obj1freeNodes.forEach(function (el) {
p.push(el);
})
obj2freeNodes.forEach(function (el) {
p.push(el);
})
var distance = [], d = {};
for (var i = 0; i <obj1freeNodes.length; i++) {
for (var j = obj1freeNodes.length; j <p.length; j++) {
var point1 = {x:p[i].x, y: p[i].y};
var point2 = {x:p[j].x, y: p[j].y};
if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
distance.push(lineDistance(point1, point2));
d[distance[distance.length - 1]] = [i, j];
}
}
}
if (distance.length == 0) {
var res = [0, obj1freeNodes.length];
} else {
res = d[Math.min.apply(Math, distance)];
}
var x1 = p[res[0]].x,
y1 = p[res[0]].y,
x4 = p[res[1]].x,
y4 = p[res[1]].y;
//remove to connecting point
for(var i=0;i<obj1freeNodes.length; i++){
if(i== res[0]){
obj1freeNodes.splice(i,1);
}
}
for(var i=0;i<obj2freeNodes.length; i++){
if(i == (res[1]-obj1freeNodes.length-1)){
obj2freeNodes.splice(i,1);
}
}
objFrom.data('freeNodes', obj1freeNodes);
objTo.data('freeNodes', obj2freeNodes);
p = [];
var path = ["M", x1.toFixed(3), y1.toFixed(3), "L", /*x2, y2, x3, y3,*/ x4.toFixed(3), y4.toFixed(3)].join(",");
if (line && line.line) {
line.bg && line.bg.attr({path: path});
line.line.attr({path: path});
} else {
var color = typeof line == "string" ? line : "#000";
var conLine = this.path(path).attr({
stroke: color,
fill: "none",
"arrow-end": "block-wide-long",
"stroke-width": 3,
});
pathObj = {
bg: bg && bg.split && this.path(path).attr({
stroke: bg.split("|")[0],
fill: "none",
"stroke-width": bg.split("|")[1] || 3
}),
line: conLine,
from: objFrom,
to: objTo,
};
return pathObj;
}
};
Raphael.el.getNodes = function()
{
//calculate the element nodes and return them
var obj = this.getBBox();
var objX = parseInt(obj.x);
var objY = parseInt(obj.y);
var objWidth = parseInt(obj.width);
var objHeight = parseInt(obj.height);
var maxCons = 8;
var scaleCut = 10;
var topPositions = [], rightPositions = [], bottomPositions = [], leftPositions = [];
//get all possible connectors based on maxCons and scaleCut
var objCons = [];
for(var i= 0; i<maxCons; i++){
topPositions.push({x: (objX + ((objWidth-scaleCut)/maxCons)*i)+i*2, y: objY-1});
rightPositions.push({x: objX + objWidth + 1, y: (objY + ((objHeight-scaleCut)/maxCons)*i)+i*2});
bottomPositions.push({x: ((objX+3) + ((objWidth-(scaleCut-2))/maxCons)*i)+i*2, y: objY + objHeight +1});
leftPositions.push({x: objX - 1, y: ((objY+4) + ((objHeight-(scaleCut+1))/maxCons)*i)+i*2});
}
// push all positions to objCons array
pushArryToArray(topPositions, objCons);
pushArryToArray(rightPositions, objCons);
pushArryToArray(bottomPositions, objCons);
pushArryToArray(leftPositions, objCons);
//this.data('freeNodes',objCons);
return objCons;
}
//pushes one array contents to another array
var pushArryToArray = function(sourceArr, targetArr){
for(var i=0; i<sourceArr.length;i++){
// draw connecting points
// R.circle(sourceArr[i].x,sourceArr[i].y,2).attr({'fill':'red', 'stroke':'none'});
targetArr.push(sourceArr[i]);
}
}
var R = Raphael("canvas","400","400");
//window.onload = function() {
start = function() {
this.ox = this.attr("x");
this.oy = this.attr("y");
this.animate({
opacity: .75
}, 500, ">");
}
move = function(dx, dy) {
this.attr({
x: this.ox + dx,
y: this.oy + dy
});
//on moving events reintializing free nodes of all events
for(var i = 0; i<arrAllEvents.length; i++){
arrAllEvents[i].data('freeNodes', arrAllEvents[i].getNodes());
}
//rendering connections on event move
for (var i = connections.length; i--;) {
R.connection(connections[i]);
}
R.safari();
}
up = function() {
}
var eventOne = R.rect(10,10,100, 100).attr({
fill: Raphael.getColor(),
stroke: "none",
cursor: "move"
});
var eventTwo = R.rect(220,10,100, 100).attr({
fill: Raphael.getColor(),
stroke: "none",
cursor: "move"
});
var eventThree = R.rect(220,220,100, 100).attr({
fill: Raphael.getColor(),
stroke: "none",
cursor: "move"
});
//intializing free connecting nodes 1st time
eventOne.data('freeNodes', eventOne.getNodes());
eventTwo.data('freeNodes', eventTwo.getNodes());
eventThree.data('freeNodes', eventThree.getNodes());
//storing all events in array
var arrAllEvents = []
arrAllEvents.push(eventOne);
arrAllEvents.push(eventTwo);
arrAllEvents.push(eventThree);
//to draw the connections b/w events
var connections = [];
connections.push(R.connection(eventOne, eventTwo, Raphael.getColor()));
connections.push(R.connection(eventOne, eventTwo, Raphael.getColor()));
connections.push(R.connection(eventOne, eventTwo, Raphael.getColor()));
connections.push(R.connection(eventOne, eventThree, Raphael.getColor()));
connections.push(R.connection(eventOne, eventThree, Raphael.getColor()));
//dragging events
for(var i = 0; i<arrAllEvents.length; i++){
arrAllEvents[i].drag(move, start, up);
}
//}
View it on Jsfiddle
Please let me know about my this effort, or to make it much more useful.