Как я могу объединить объекты в библиотеке Raphael Javascript?

Извините за длинный вопрос, но здесь идет. Я пытаюсь изменить формы перетаскивания вокруг демо здесь:

http://raphaeljs.com/graffle.html

Демо работает отлично. То, что я хочу сделать, это поместить слова в фигуры и переместить фигуру и текст вокруг как составной отдельный объект.

Вот код для создания объектов:

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"));
};

Я попробовал что-то вроде этого:

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

и внесли изменения в другом месте, чтобы он работал, но затем он просто перемещает текст и фигуры, но фигура и текст никогда не видятся в целом. Я могу переместить текст отдельно от фигуры и наоборот. Мне нужно, чтобы они были одним объектом. поэтому они двигаются вместе. Как я могу это сделать? Спасибо за любую помощь.

РЕДАКТИРОВАТЬ:

Я попробовал это:

  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))

Но текст и форма не остались вместе, когда я переместил форму. Текст просто остался на месте.

РЕДАКТИРОВАТЬ: я не могу получить демонстрационную версию на http://raphaeljs.com/graffle.html для работы с Chrome. IE это работает.

4 ответа

Решение

Сделал серьезное редактирование, чтобы связать элементы более элегантным способом.


Наборы хороши для группировки объектов Raphael, но наборы не создают свои собственные элементы, поэтому вы не можете перетаскивать набор, поскольку при нажатии на холст вы выбираете либо форму, либо текст, но не набор (поскольку без заданного элемента).

Вот простой jsFiddle, показывающий свойства набора. Обратите внимание, что набор не имеет x или же y свойства.

Из документации Рафаэля:

[Набор c] устанавливает объект, подобный массиву, чтобы сохранить и управлять несколькими элементами одновременно. Предупреждение: он не создает никаких элементов для себя на странице.


Простой обходной путь заключается в том, чтобы сделать текст и форму отдельно перетаскиваемыми. Затем переместите связанный текст вместе с формой... и связанную форму вместе с текстом.

Связать подобные объекты просто... создать свойство. В этом случае каждая фигура и каждый текст имеют свойство, называемое .pair которая является ссылкой на связанный элемент.

Вот как это делается:

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;
}

А затем в коде перетаскивания, который является move(), dragger(), а также up() функции, вы должны убедиться, что имеете дело как с элементом, на который нажали, так и с соответствующим элементом.

Например, здесь уместная часть move() функция. Обратите внимание, что text можно рассматривать так же, как rectangle (путем изменения атрибутов x а также y), Итак false условие в каждом из условных операторов Javascript ниже обрабатывает оба случая для rectangle и для 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);
    ...
}


И ниже полный рабочий код:

Рабочий пример jsFiddle перетаскиваемого текста и фигур

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"));
};​

Для полноты здесь приведен код для ссылки на jsFiddle для отображения свойств набора:

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's object
// 1 : Raphael's object
// items : Raphael's object,Raphael's object
// length : 2
// type : set

Или попробуйте этот "групповой" плагин для Raphael, который позволяет вам создать правильный элемент SVG group.

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

Да вот что set Объект предназначен для:

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);

Дополнительный ответ:

Хорошо, я вижу, что вы пытались использовать set, но вы используете его неправильно. Набор создает группу вещей. Точно так же, как если бы вам нужно было группировать фигуры и текст в Adobe Illustrator, Inkscape, Microsoft Word или Open Office Если я вас правильно понимаю, то, что вы хотите, это:

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))
         ];

Вам также придется изменить функции перетаскивания и перемещения, поскольку фигуры больше не имеют тип 'rect', а имеют тип '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();
};

Все комплекты имеют x а также y атрибутов.

Не проще ли изменить атрибуты парного объекта вместе с атрибутами, которые меняются при перетаскивании основного объекта?

Что-то вроде этого:

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);




    };
Другие вопросы по тегам