I've created a plugin for Raphael JS. It basically allows you to call
paper.connect(obj1,obj2,colour)
That draws a line between the two objects and maintains the line when the objects are animated. This is what I've come up with so far. It works but its not very performant, any suggestions on what else I could do to achieve the same thing.
Raphael.fn.connect = function(obj1, obj2, colour) {
// list of paths each object has
obj1.connections = []
obj2.connections = []
// get the bounding box of each object
var box1 = obj1.getBBox()
var box2 = obj2.getBBox()
// create a line/path from object 1 to object 2
var p = this.path("M" + (box1.x + box1.width / 2) + ","
+ (box1.y + box1.height / 2) + "L" + (box2.x + box2.width / 2)
+ "," + (box2.y + box2.height / 2))
// adjust attributes of the path
p.attr({
stroke : colour,
"stroke-linecap" : "round",
"stroke-opacity" : Math.max(obj1.attr('opacity'), obj2.attr('opacity'))
})
// set the start and end element for this path
p.startElement = obj1;
p.endElement = obj2;
// add the path to each of the object
obj1.connections.push(p)
obj2.connections.push(p)
// mark each object as being connected
obj1.connected = true;
obj2.connected = true;
// listen for the Raphael frame event
eve.on("raphael.anim.frame.*", function(obj) {
// if the object the frame event is fired on is connected
if (this.connected) {
// for each connection on this object
for ( var c in this.connections) {
var path = this.connections[c]; // temp path
var b1 = path.startElement.getBBox(); // get the current
// location of start
// element
var b2 = path.endElement.getBBox();// get the current location
// of end element
// move the path to the new locations
path.attr({
path : "M " + (b1.x + b1.width / 2) + " "
+ (b1.y + b1.height / 2) + "L "
+ (b2.x + b2.width / 2) + " "
+ (b2.y + b2.height / 2),
opacity : Math.max(path.startElement.attr('opacity'),
path.endElement.attr('opacity'))
});
}
}
});
}
Not convinced this is the best way at all, but this is my first time using Raphael so I just did all that from looking at the Raphael source code...
In our app we have a line tool. We drop a line with 2 movable end points onto our paper.
All shapes in our app have an associated VisualModel that contains all the geometric data inside of them. These VisualModels also double as Actors. Any Actor can subscribe to any other Actor and when changes occur all interested parties respond.
A system like this allows for a line's path to change via a redraw function that gets called anytime the 2 connected objects modify their X/Y coordinates.
redraw: function() {
var x1 = this.shapeView1.visualModel.get('x'),
y1 = this.shapeView1.visualModel.get('y'),
x2 = this.shapeView2.visualModel.get('x'),
y2 = this.shapeView2.visualModel.get('y'),
pathData;
pathData = 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2;
this.line.attr({
path: pathData,
fill: '#000000',
stroke: LineConstants.COLOR,
'stroke-width': LineConstants.THICKNESS
});
}
We created "movable" mixin. This mixin would allow you to add movability to your shapes. This mixin would update the x/y coordinates in and in turn trigger a 'change' event that your line class would pick up.
handleDraggging: function(delta) {
this.shape.move(delta);
}
move: function(delta) {
//... compute movement based on delta
this.visualModel.set('x', xPosition);
this.visualModel.set('y', yPosition);
}
initialize: function(shapeView1, shapeView2) {
// ...
this.shapeView1 = shapeView1;
this.shapeView2 = shapeView2;
this.listenTo(shapeView1.visualModel, 'change:x change:y', this.redraw);
this.listenTo(shapeView2.visualModel, 'change:x change:y', this.redraw);
}
Performance for this is great. You can take a look at it in action by going to eventbrite.com, create an event, enabling reserved seating (step 2), add a new map, click 'objects' on the left and drop a line on the paper.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With