使用Raphael JS拖动,放下和塑造旋转

时间:2011-11-13 23:39:38

标签: drag-and-drop raphael rotation

我正在使用RaphaelJS 2.0在div中创建多个形状。每个形状都需要能够独立地在div的范围内拖放。双击形状后,该形状需要旋转90度。然后可以拖放它并再次旋转。

我已经在fiddler上加载了一些代码:http://jsfiddle.net/QRZMS/。基本上就是这样:

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


    }

拖放工作正常,其中一个形状在双击时旋转。但是,有两个问题/问题:

  1. 如何自动将旋转附加到每个形状上,而无需将每个项目参考硬编码到旋转方法中?即我只想绘制一次形状,然后将它们全部自动暴露给相同的行为,这样它们就可以独立地拖放/旋转,而不必将该行为明确地应用于每个形状。

  2. 旋转形状后,它不再正确拖动 - 就好像拖动鼠标移动与形状的原始方向相关,而不是在旋转形状时更新。我怎样才能让它正常工作,以便可以多次拖动和旋转形状,Seamlessley?

  3. 非常感谢任何指示!

6 个答案:

答案 0 :(得分:13)

我已经多次尝试过围绕新的变换引擎,但无济于事。所以,我已经回到了第一原则。

在尝试计算出不同变换的影响后,我终于设法正确地拖放了经历了几次变换的对象 - t,T,... t,... T,r,R等...

所以,这是解决方案的关键

var ox = 0;
var oy = 0;

function drag_start(e) 
{
};


function drag_move(dx, dy, posx, posy) 
{


   r1.attr({fill: "#fa0"});

   //
   // Here's 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
}

就是这样。愚蠢的简单,我确信它已经发生在很多人身上,但也许有人会发现它很有用。

这是一个fiddle供你玩。

... this是初始问题的有效解决方案。

答案 1 :(得分:4)

我通过在值更改时重新应用所有转换来解决拖动/旋转问题。我为它创建了一个插件。

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

在这里演示:

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

答案 2 :(得分:3)

正如amadan所说,当多个事物具有相同(初始)属性/属性时,创建函数通常是个好主意。这确实是你第一个问题的答案。至于第二个问题,这有点棘手。

旋转Rapheal对象时,坐标平面也是如此。出于某种原因,dmitry和网上的一些其他来源似乎都认为这是实施它的正确方法。我和你一样,不同意。我没有找到一个全面的好解决方案,但我做了一个工作来创造一个解决方案。我将简要解释然后显示代码。

  • 创建自定义属性以存储当前的旋转状态
  • 根据该属性,您决定如何处理移动。

假设您只是将形状旋转90度(如果不是变得更难),您可以确定如何操纵坐标。

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

我真的无法用简洁的方式来解释drag_move的工作原理。我认为你最好看看代码并看看它是如何工作的。基本上,您只需要弄清楚如何从这个新的旋转状态处理x和y变量。没有我绘制大量图形,我不确定我是否足够清楚。 (我做了很多事情,试着弄清楚应该做些什么)。

这种方法有一些缺点:

  • 它仅适用于90度旋转(需要进行大量计算才能完成45度,无需任何给定程度)
  • 旋转后拖动开始时有轻微的移动。这是因为拖动采用已旋转的旧x和y值。对于这种形状的尺寸来说,这不是一个大问题,但更大的形状你会真正开始注意到形状在画布上跳跃。

我假设您使用变换的原因是您可以为旋转设置动画。如果没有必要,那么你可以使用.rotate()函数,该函数总是围绕元素的中心旋转,因此将消除我提到的第二个缺点。

这不是一个完整的解决方案,但它绝对应该让你走正确的道路。我很想看到一个完整的工作版本。

我还在jsfiddle上创建了一个版本,你可以在这里查看:http://jsfiddle.net/QRZMS/3/

祝你好运。

答案 3 :(得分:1)

我通常会为我的形状创建一个对象,并将事件处理写入对象。

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

在上面,构造函数不仅创建了矩形,还设置了双击事件。

需要注意的一点是,对象的引用存储在&#34;&#34;中。这是因为&#34;这个&#34;参考变化取决于范围。在dblClick函数中,我需要从对象中引用 rect angle 值,因此我使用存储的引用 that.rect 和<强> that.angle

请参阅此example(从稍微狡猾的先前实例更新)

可能有更好的方法来做你需要的事情,但这应该适合你。

希望有所帮助,

尼克


附录:Dan,如果你真的坚持这一点,并且没有Raphael2给你的一些东西,我建议你回到拉斐尔1.5。 X。变换刚刚添加到Raphael2中,旋转/翻译/缩放代码在1.5.2中完全不同(并且更容易)。

看着我,更新我的帖子,希望有业力......

答案 4 :(得分:1)

如果您不想使用ElbertF库,可以在极坐标中转换笛卡尔坐标。

必须添加或删除角度并在笛卡尔坐标系中再次变换。

我们可以看到这个例子,直接旋转隆隆声并移动。

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

<强>样本
http://jsfiddle.net/Ef83k/74/

答案 5 :(得分:0)

我的第一个想法是使用getBBox(false)来捕获变换后对象的x,y坐标,然后从画布中删除原始Raphael obj的theChild(),然后使用getBBox中的坐标数据重绘对象(false )。一个黑客,但我有它的工作。

但需注意一点:因为getBBox(false)返回的对象是对象的CORNER坐标(x,y),你需要通过这样做来计算重绘对象的中心...   x = box ['x'] +(box ['width'] / 2);   y = box ['y'] +(box ['height'] / 2);

,其中 box = shapeObj.getBBox(false);

解决同样问题的另一种方法