Drag, Drop, and Rotate Forms Using Raphael JS

I am using RaphaelJS 2.0 to create multiple shapes in a div. Each form must be linked and omitted within the div independently. When you double-click on a shape, this shape should rotate 90 degrees. Then it can be dragged and dropped again.

I uploaded the code to the script: http://jsfiddle.net/QRZMS/ . This is basically this:

window.onload = function () { var angle = 0; var R = Raphael("paper", "100%", "100%"), shape1 = R.rect(100, 100, 100, 50).attr({ fill: "red", stroke: "none" }), shape2 = R.rect(200, 200, 100, 50).attr({ fill: "green", stroke: "none" }), shape3 = R.rect(300, 300, 100, 50).attr({ fill: "blue", stroke: "none" }), shape4 = R.rect(400, 400, 100, 50).attr({ fill: "black", stroke: "none" }); var start = function () { this.ox = this.attr("x"); this.oy = this.attr("y"); }, move = function (dx, dy) { this.attr({ x: this.ox + dx, y: this.oy + dy }); }, up = function () { }; R.set(shape1, shape2, shape3, shape4).drag(move, start, up).dblclick(function(){ angle -= 90; shape1.stop().animate({ transform: "r" + angle }, 1000, "<>"); }); } 

Drag and drop works, and one of the shapes rotates with a double click. However, there are two problems / questions:

  • How can I automatically attach a rotation to each shape without the need for hard-coding each link element in the rotation method? That is, I just want to draw the shapes once, and then all of them are automatically subjected to the same behavior, so they can be dragged / dropped / rotated independently, without explicitly applying this behavior to each shape.

  • After the shape has been rotated, it is no longer draggable - as if the movement of the dragging mouse touched the original orientation of the shape, and not the adjustment when the shape was rotated. How can I get this to work correctly so that shapes can simply be dragged and rotated many times, seamlessly?

Thanks so much for any pointers!

+4
source share
6 answers

I tried several times to wrap my head around a new transformation mechanism, but to no avail. So, I returned to the first principles.

Finally, I managed to correctly drag an object that underwent several transformations, trying to understand the effects of various transformations - t, T, ... t, ... T, r, R, etc ....

So here is the gist of the solution

 var ox = 0; var oy = 0; function drag_start(e) { }; function drag_move(dx, dy, posx, posy) { r1.attr({fill: "#fa0"}); // // Here the interesting part, apply an absolute transform // with the dx,dy coordinates minus the previous value for dx and dy // r1.attr({ transform: "...T" + (dx - ox) + "," + (dy - oy) }); // // store the previous versions of dx,dy for use in the next move call. // ox = dx; oy = dy; } function drag_up(e) { // nothing here } 

What is it. It’s deeply simple, and I’m sure that this happened to a lot of people, but it might seem useful to someone.

Here's a fiddle you can play with.

... and this is a working solution for the initial question.

+13
source

I solved the drag / rotate problem by reapplying all the transforms when the value changed. I created a plugin for it.

https://github.com/ElbertF/Raphael.FreeTransform

Demo here:

http://alias.io/raphael/free_transform/

+4
source

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.

+3
source

I usually create an object for my figure and write event processing to the object.

 function shape(x, y, width, height, a) { var that = this; that.angle = 0; that.rect = R.rect(x, y, width, height).attr(a); that.rect.dblclick(function() { that.angle -= 90; that.rect.stop().animate({ transform: "r" + that.angle }, 1000, "<>"); }); return that; } 

In the above example, the constructor not only creates a rectangle, but also sets a double-click event.

It should be noted that the reference to the object is stored in "this". This is because the "this" link changes depending on the scope. In the dblClick function, I need to reference the rect values ​​and the angle from my object, so I use the stored reference that.rect and that.angle

See this example (updated from a slightly dodgy previous instance)

There may be better ways to do what you need, but this should work for you.

Hope this helps,

Nick


Appendix: Dan, if you are really stuck on this and can live without some of the things that Raphael2 gives you, I would recommend going back to Raphael 1.5.x. Transforms were just added to Raphael2, the rotation / translation / scaling code is completely different (and easier) in 1.5.2.

Look at me, updating your post, hoping for karma ...

+1
source

If you do not want to use the ElbertF library, you can convert the Cartesian coordinates to polar coordinates.

After you have to add or remove the angle and convert it again to the Cartesian coordinate.

We can see this example with direct rotation in the horn and movement.

HTML

 <div id="foo"> </div> 

JAVASCRIPT

 var paper = Raphael(40, 40, 400, 400); var c = paper.rect(40, 40, 40, 40).attr({ fill: "#CC9910", stroke: "none", cursor: "move" }); c.transform("t0,0r45t0,0"); var start = function () { this.ox = this.type == "rect" ? this.attr("x") : this.attr("cx"); this.oy = this.type == "rect" ? this.attr("y") : this.attr("cy"); }, move = function (dx, dy) { var r = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); var ang = Math.atan2(dy,dx); ang = ang - Math.PI/4; dx = r * Math.cos(ang); dy = r * Math.sin(ang); var att = this.type == "rect" ? { x: this.ox + dx, y: this.oy + dy} : { cx: this.ox + dx, cy: this.oy + dy }; this.attr(att); }, up = function () { }; c.drag(move, start, up);? 

Demo
http://jsfiddle.net/Ef83k/74/

+1
source

My first thought was to use getBBox (false) to capture the x, y coordinates of the object after the conversion, then removeChild () the original Raphael obj from the canvas, and then redraw the object using the coordinate data from getBBox (false). hack, but it works for me.

one note: since the object returned by getBBox (false) is the coordinates of CORNER (x, y) of the object you need to calculate the center of the re-drawn object by doing ... x = field ['x'] + (field [' width '] / 2); y = box ['y'] + (field ['height'] / 2);

where box = shapeObj.getBBox (false);

another way to solve the same problem

0
source

Source: https://habr.com/ru/post/1381048/


All Articles