How to combine objects in Raphael JavaScript library?

Sorry for the long question, but here it goes. I am trying to change the drag and drop forms around the demo here:

http://raphaeljs.com/graffle.html

Demo works fine. What I want to do is put the words inside the shapes and move the shape and text around as a composite single object.

Here is the code to create the objects:

window.onload = function () { var dragger = function () { this.ox = this.type == "rect" ? this.attr("x") : this.attr("cx"); this.oy = this.type == "rect" ? this.attr("y") : this.attr("cy"); this.animate({"fill-opacity": .2}, 500); }, move = function (dx, dy) { var att = this.type == "rect" ? {x: this.ox + dx, y: this.oy + dy} : {cx: this.ox + dx, cy: this.oy + dy}; this.attr(att); for (var i = connections.length; i--;) { r.connection(connections[i]); } r.safari(); }, up = function () { this.animate({"fill-opacity": 0}, 500); }, r = Raphael("holder", 640, 480), connections = [], shapes = [ r.ellipse(190, 100, 30, 20), r.rect(290, 80, 60, 40, 10), r.rect(290, 180, 60, 40, 2), r.ellipse(450, 100, 20, 20) ]; for (var i = 0, ii = shapes.length; i < ii; i++) { var color = Raphael.getColor(); shapes[i].attr({fill: color, stroke: color, "fill-opacity": 0, "stroke-width": 2, cursor: "move"}); shapes[i].drag(move, dragger, up); } connections.push(r.connection(shapes[0], shapes[1], "#fff")); connections.push(r.connection(shapes[1], shapes[2], "#fff", "#fff|5")); connections.push(r.connection(shapes[1], shapes[3], "#000", "#fff")); }; 

I tried something like this:

  myWords = [ r.text(190, 100, "Hello"), r.text(480,100, "Good Bye") ]; 

and made adjustments elsewhere to make it work, but then it just moves the text and shapes, but the shape and text are never visible in general. I can move the text separately from the form and vice versa. I need them to be one object. therefore they move together. How can i do this? Thanks for any help.

EDIT:

I tried this:

  st.push(r.text (190, 100, "node1"), r.ellipse(190, 100, 30, 20)), st.push(r.text (290, 80, "Center"), r.rect(290, 80, 60, 40, 10)), st.push(r.text (290, 180, "node2"), r.rect(290, 180, 60, 40, 2)), st.push(r.text (450, 100, "node3"), r.ellipse(450, 100, 20, 20)) 

But the text and the form did not stay together when I moved the form. The text just left.

EDIT: I can’t get a demo of http://raphaeljs.com/graffle.html for working with Chrome. IE is working.

+43
javascript raphael
Sep 09 '10 at 18:26
source share
4 answers

Did a lot of editing help the elements more elegantly.




The settings are good for grouping Raphael objects, but the sets do not create their own elements, so you cannot drag the set, because when you click on the canvas, you either select a shape or text, but never set (because there is no element set).

Here is a simple jsFiddle showing the properties of a set. Note that the set does not have x or y properties.

From Raphael's doc:

[Set c] creates an array that looks like an object for storing and managing multiple elements at the same time. Warning: it does not create any elements for itself on the page.


A simple job is to make the text and form separately draggable. Then move the linked text along with the form ... and the associated form along with the text.

Linking such objects is easy ... to create a property. In this case, each form and each text have a .pair property, which is a link to an associated element.

Here's how to do it:

 var i, ii, tempS, tempT shapes = [ ... ], texts = [ ... ]; for (i = 0, ii = shapes.length; i < ii; i++) { tempS = shapes[i].attr( ... ); tempT = texts[i].attr( ...); // Make all the shapes and texts dragable shapes[i].drag(move, dragger, up); texts[i].drag(move, dragger, up); // Associate the elements tempS.pair = tempT; tempT.pair = tempS; } 

And then in the drag and drop code, which is the functions move() , dragger() and up() , you need to make sure that you are dealing with both the clicked on element and its associated element.

For example, here is the corresponding part of the move() function. Note that text can be treated in the same way as a rectangle (by changing the x and y attributes), therefore the false condition in each of the conditional Javascript statements below treats as a case for a rectangle and for text

 move = function (dx, dy) { // Move main element var att = this.type == "ellipse" ? {cx: this.ox + dx, cy: this.oy + dy} : {x: this.ox + dx, y: this.oy + dy}; this.attr(att); // Move paired element att = this.pair.type == "ellipse" ? {cx: this.pair.ox + dx, cy: this.pair.oy + dy} : {x: this.pair.ox + dx, y: this.pair.oy + dy}; this.pair.attr(att); ... } 


And below is the full working code:

JsFiddle working example for draggable text and shapes

 Raphael.fn.connection = function (obj1, obj2, line, bg) { if (obj1.line && obj1.from && obj1.to) { line = obj1; obj1 = line.from; obj2 = line.to; } var bb1 = obj1.getBBox(), bb2 = obj2.getBBox(), p = [{x: bb1.x + bb1.width / 2, y: bb1.y - 1}, {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + 1}, {x: bb1.x - 1, y: bb1.y + bb1.height / 2}, {x: bb1.x + bb1.width + 1, y: bb1.y + bb1.height / 2}, {x: bb2.x + bb2.width / 2, y: bb2.y - 1}, {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + 1}, {x: bb2.x - 1, y: bb2.y + bb2.height / 2}, {x: bb2.x + bb2.width + 1, y: bb2.y + bb2.height / 2}], d = {}, dis = []; for (var i = 0; i < 4; i++) { for (var j = 4; j < 8; j++) { var dx = Math.abs(p[i].x - p[j].x), dy = Math.abs(p[i].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))) { dis.push(dx + dy); d[dis[dis.length - 1]] = [i, j]; } } } if (dis.length == 0) { var res = [0, 4]; } else { res = d[Math.min.apply(Math, dis)]; } var x1 = p[res[0]].x, y1 = p[res[0]].y, x4 = p[res[1]].x, y4 = p[res[1]].y; dx = Math.max(Math.abs(x1 - x4) / 2, 10); dy = Math.max(Math.abs(y1 - y4) / 2, 10); var x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3), y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3), x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3), y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3); var path = ["M", x1.toFixed(3), y1.toFixed(3), "C", 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"; return { bg: bg && bg.split && this.path(path).attr({stroke: bg.split("|")[0], fill: "none", "stroke-width": bg.split("|")[1] || 3}), line: this.path(path).attr({stroke: color, fill: "none"}), from: obj1, to: obj2 }; } }; var el; window.onload = function () { var color, i, ii, tempS, tempT, dragger = function () { // Original coords for main element this.ox = this.type == "ellipse" ? this.attr("cx") : this.attr("x"); this.oy = this.type == "ellipse" ? this.attr("cy") : this.attr("y"); if (this.type != "text") this.animate({"fill-opacity": .2}, 500); // Original coords for pair element this.pair.ox = this.pair.type == "ellipse" ? this.pair.attr("cx") : this.pair.attr("x"); this.pair.oy = this.pair.type == "ellipse" ? this.pair.attr("cy") : this.pair.attr("y"); if (this.pair.type != "text") this.pair.animate({"fill-opacity": .2}, 500); }, move = function (dx, dy) { // Move main element var att = this.type == "ellipse" ? {cx: this.ox + dx, cy: this.oy + dy} : {x: this.ox + dx, y: this.oy + dy}; this.attr(att); // Move paired element att = this.pair.type == "ellipse" ? {cx: this.pair.ox + dx, cy: this.pair.oy + dy} : {x: this.pair.ox + dx, y: this.pair.oy + dy}; this.pair.attr(att); // Move connections for (i = connections.length; i--;) { r.connection(connections[i]); } r.safari(); }, up = function () { // Fade original element on mouse up if (this.type != "text") this.animate({"fill-opacity": 0}, 500); // Fade paired element on mouse up if (this.pair.type != "text") this.pair.animate({"fill-opacity": 0}, 500); }, r = Raphael("holder", 640, 480), connections = [], shapes = [ r.ellipse(190, 100, 30, 20), r.rect(290, 80, 60, 40, 10), r.rect(290, 180, 60, 40, 2), r.ellipse(450, 100, 20, 20) ], texts = [ r.text(190, 100, "One"), r.text(320, 100, "Two"), r.text(320, 200, "Three"), r.text(450, 100, "Four") ]; for (i = 0, ii = shapes.length; i < ii; i++) { color = Raphael.getColor(); tempS = shapes[i].attr({fill: color, stroke: color, "fill-opacity": 0, "stroke-width": 2, cursor: "move"}); tempT = texts[i].attr({fill: color, stroke: "none", "font-size": 15, cursor: "move"}); shapes[i].drag(move, dragger, up); texts[i].drag(move, dragger, up); // Associate the elements tempS.pair = tempT; tempT.pair = tempS; } connections.push(r.connection(shapes[0], shapes[1], "#fff")); connections.push(r.connection(shapes[1], shapes[2], "#fff", "#fff|5")); connections.push(r.connection(shapes[1], shapes[3], "#000", "#fff")); };​ 



For completeness, here is the code associated with jsFiddle for displaying set properties :

 window.onload = function () { var paper = Raphael("canvas", 320, 200), st = paper.set(), propArr = []; st.push( paper.circle(10, 10, 5), paper.circle(30, 10, 5) ); st.attr({fill: "red"}); for(var prop in st) { if (st.hasOwnProperty(prop)) { // handle prop as required propArr.push(prop + " : " + st[prop]); } } alert(propArr.join("\n")); };​ // Output: // 0 : Raphael object // 1 : Raphael object // items : Raphael object,Raphael object // length : 2 // type : set 
+55
Oct 06 2018-10-06
source share

Or try this Raphael "group" plugin, which allows you to create the right SVG group element.

https://github.com/rhyolight/Raphael-Plugins/blob/master/raphael.group.js

+7
May 25 '12 at 19:54
source share

Yes, what the set object is for:

 var myWords = r.set(); myWords.push( r.text(190, 100, "Hello"), r.text(480,100, "Good Bye" ); // now you can treat the set as a single object: myWords.rotate(90); 



Additional answer:

Ok, I see that you tried using set, but you are using it incorrectly. A set creates a group of things. Just as if you were grouping shapes and text in Adobe Illustrator or Inkscape, Microsoft Word, or Open Office. If I understand correctly what you want is:

 shapes = [ r.set(r.text (190, 100, "node1"), r.ellipse(190, 100, 30, 20)), r.set(r.text (290, 80, "Center"), r.rect(290, 80, 60, 40, 10)), r.set(r.text (290, 180, "node2"), r.rect(290, 180, 60, 40, 2)), r.set(r.text (450, 100, "node3"), r.ellipse(450, 100, 20, 20)) ]; 

You will also have to change your dragger and move functions, since the forms are no longer of the type 'rect', but of the type 'set':

 var dragger = function () { this.ox = this.attr("x"); this.oy = this.attr("y"); this.animate({"fill-opacity": .2}, 500); }; var move = function (dx, dy) { var att = {x: this.ox + dx, y: this.oy + dy}; this.attr(att); for (var i = connections.length; i--;) { r.connection(connections[i]); } r.safari(); }; 

All sets have attributes x and y .

+4
Sep 09 '10 at 19:22
source share

Wouldn’t it be easier to change the attributes of a paired object along with the attributes that change when you drag the main object?

Something like that:

 window.onload = function () { var R = Raphael("holder"), circ = R.circle(100, 100, 50).attr({ "fill": "#d9d9d9", "stroke-width": 1 }), circ2 = R.circle(50, 50, 5), start = function () { this.ox = this.attr("cx"); //ox = original x value this.oy = this.attr("cy"); this.animate({ "opacity": .5, "stroke-width": 15 }, 200); }, move = function (dx, dy) { //dx - delta x - diiference in movement between point a and b var cdx = circ2.attr("cx") - this.attr("cx"), cdy = circ2.attr("cy") - this.attr("cy"); this.attr({ "cx": this.ox + dx, "cy": this.oy + dy }); group(this,circ2,cdx,cdy); R.safari(); }, up = function () { this.animate({ "opacity": 1, "stroke-width": 1 }, 200); }, group = function (refObj,thisObj, dx, dy) { thisObj.attr({ "cx": refObj.attr("cx") + dx, "cy": refObj.attr("cy") + dy }); }; circ.drag(move, start, up); }; 
+1
Jul 20 '11 at 9:02
source share



All Articles