【问题标题】:How can I combine objects in the Raphael javascript library?如何在 Raphael javascript 库中组合对象?
【发布时间】:2010-09-09 18:26:17
【问题描述】:

抱歉,问了一个很长的问题,但这里是。我正在尝试在此处修改演示周围的拖动形状:

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 可以。

【问题讨论】:

  • 您不移动形状。你移动集合。将集合视为恰好包含文本和矩形的单个形状。
  • @slebetman - 你会怎么做?没有办法点击一个集合来选择它来移动,因为集合没有关联的 DOM 元素。 - @johnny - 您可以使用自定义属性对元素进行配对,而不是使用集合的复杂方法。 - 看我的回答。

标签: javascript raphael


【解决方案1】:

进行了重大编辑,以更优雅的方式关联元素。


Sets 适用于对 Raphael 对象进行分组,但集合不会创建自己的元素,因此您不能拖放集合,因为当您单击画布时,您可以选择形状或文本,但绝不是集合(因为没有集合元素)。

Here is a simple jsFiddle showing the properties of a set. 请注意,集合没有 xy 属性。

来自Raphael documentation

[A set 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 相同的方式处理(通过更改属性xy),因此下面每个Javascript 条件运算符中的false 条件处理@ 的情况987654341@ 和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);
    ...
}


下面是完整的工作代码:

Working jsFiddle example of 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"));
};​

为了完整起见,这里是 the linked to jsFiddle for showing the properties of a set 的代码:

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 编写的补丁以允许命名集:stackoverflow.com/questions/6277129/… - 您可以从事件回调。感谢彼得的出色工作;我的解决方案直接受到了这个答案的启发。
  • 我不知道为什么这些不再在 Chrome 中工作,但是哦。在 IE 中仍然可以正常工作。
  • 我想,最好将链接粘贴到 raphael 网站 (raphaeljs.com/graffle.html) 上的示例,然后从那里获取所有代码并将其粘贴到此处和 jsfiddle
【解决方案2】:

或者,试试这个 Raphael 的“组”插件,它可以让你创建一个合适的 SVG 组元素。

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

【讨论】:

    【解决方案3】:

    是的,这就是 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 但您使用错误。一个集合创建一组事物。就像您要在 Adob​​e 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();
    };
    

    所有集合都有xy 属性。

    【讨论】:

    • 谢谢。不幸的是,它没有用。我可以移动形状但不能移动文本。我改变了你所说的一切。目前我所知道的只有没有形状的 r.text 。非常感谢您的帮助。
    • 这个方法的问题是集合不会在页面上创建任何元素,所以当你点击拖放时你不会选择集合,而只会选择元素....所以dragger 中的this 指的是一个元素而不是集合。
    • Sets 没有xy 的集合。对set 的操作将应用于集合中的每个人。 set 不被视为一个整体。 ------------ myWords.rotate(90); 不会整体旋转set。它简单地迭代集合中的每个对象并旋转那些 ==> jsfiddle.net/UFxJZ(请注意,这 2 个单词在 2 条单独的线上开始平行......旋转后它们现在平行但在同一条线上!)
    • 我们有两个似乎相互矛盾的答案。一个说一个集合将允许您一致地移动对象,另一个说一个集合将不允许该功能。对于哪个答案是正确的,我们能得到一个明确的答案吗?他们不可能都对吧?然而,他们都有赞成票
    【解决方案4】:

    只更改配对对象的属性以及拖动主对象时更改的属性不是更容易吗?

    类似这样的:

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

    【讨论】:

    • 如果您只在其中一个分组元素上调用拖动并且它们具有重叠的边界,则“sidecar”元素可能会获得鼠标事件并且不允许拖动主要元素。使用 toFront() 并没有为我解决这个问题,但是对组中的所有人调用 drag 效果很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-26
    • 1970-01-01
    • 2022-06-14
    • 2012-08-06
    • 2020-07-27
    • 2020-02-14
    相关资源
    最近更新 更多