As Amadan suggests, it is usually useful to create functions when several things have the same (initial) attributes / properties. This is really the answer to your first question. Regarding the second question, this is a bit more complicated.
When the Rapheal object rotates, then the coordinate plane. For some reason, dmitry and several other sources on the Internet seem to agree that this is the right way to implement it. I, like you, do not agree. I could not find a good solution, but I managed to create a job. I will briefly explain and then show the code.
- Creating a custom attribute to save the current state of rotation
- Depending on this attribute, you decide how to handle this movement.
Assuming that you will only rotate the shapes 90 degrees (if that doesn't get much harder), you can determine how coordinates should be manipulated.
var R = Raphael("paper", "100%", "100%"); //create the custom attribute which will hold the current rotation of the object {0,1,2,3} R.customAttributes.rotPos = function (num) { this.node.rotPos = num; }; var shape1 = insert_rect(R, 100, 100, 100, 50, { fill: "red", stroke: "none" }); var shape2 = insert_rect(R, 200, 200, 100, 50, { fill: "green", stroke: "none" }); var shape3 = insert_rect(R, 300, 300, 100, 50, { fill: "blue", stroke: "none" }); var shape4 = insert_rect(R, 400, 400, 100, 50, { fill: "black", stroke: "none" }); //Generic insert rectangle function function insert_rect(paper,x,y, w, h, attr) { var angle = 0; var rect = paper.rect(x, y, w, h); rect.attr(attr); //on createion of the object set the rotation position to be 0 rect.attr({rotPos: 0}); rect.drag(drag_move(), drag_start, drag_up); //Each time you dbl click the shape, it gets rotated. So increment its rotated state (looping round 4) rect.dblclick(function(){ var pos = this.attr("rotPos"); (pos++)%4; this.attr({rotPos: pos}); angle -= 90; rect.stop().animate({transform: "r" + angle}, 1000, "<>"); }); return rect; } //ELEMENT/SET Dragger functions. function drag_start(e) { this.ox = this.attr("x"); this.oy = this.attr("y"); }; //Now here is the complicated bit function drag_move() { return function(dx, dy) { //default position, treat drag and drop as normal if (this.attr("rotPos") == 0) { this.attr({x: this.ox + dx, y: this.oy + dy}); } //The shape has now been rotated -90 else if (this.attr("rotPos") == 1) { this.attr({x:this.ox-dy, y:this.oy + dx}); } else if (this.attr("rotPos") == 2) { this.attr({x: this.ox - dx, y: this.oy - dy}); } else if (this.attr("rotPos") == 3) { this.attr({x:this.ox+dy, y:this.oy - dx}); } } }; function drag_up(e) { }
I can’t think of an understandable short way to explain how drag_move works. It seems to me that it is best to look at the code and see how it works. Basically, you just need to figure out how the x and y variables from this new rotated state are now processed. Without me, drawing a lot of graphics, I'm not sure I can be clear enough. (I turned my head sideways to understand what he should do).
There are several drawbacks to this method:
- It only works for 90-degree rotations (it will take a huge amount of computation to do 45 degrees, not counting any particular degree).
- After turning there is a slight movement. This is because the drag and drop takes the old x and y values that were rotated. This is not a serious problem for a shape that size, but the larger shapes you really start to notice shapes jumping over the canvas.
I assume that you are using a transform so that you can animate the rotation. If this is not necessary, you can use the .rotate() function, which always rotates around the center of the element and therefore eliminates the second flaw that I mentioned.
This is not a complete solution, but it must definitely make you go the right way. I would be interested to see the full working version.
I also created a version of this on jsfiddle, which you can see here: http://jsfiddle.net/QRZMS/3/
Good luck.