拖放和形状旋转与拉斐尔JS



我使用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. 在一个形状被旋转后,它不再正确地拖动-好像鼠标拖动移动与形状的原始方向有关,而不是在形状被旋转时更新。我怎样才能让这个工作正确,使形状可以拖动和旋转许多次,无缝?

非常感谢任何提示!

我已经尝试了几次,把我的头围绕在新的转换引擎,但无济于事。所以,我回到了第一原则。

我终于成功地拖放了一个对象,经历了几次转换,试图找出不同的转换的影响- 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
}

就是这样。愚蠢的简单,我相信很多人已经想到了,但也许有人会觉得它很有用。

给你一把小提琴。

…这是初始问题的解。

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

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

演示:

http://alias。io/raphael/free_transform/

正如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/

好运。

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

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;
}
在上面的例子中,构造函数不仅创建了矩形,还设置了双击事件。

需要注意的一点是,对象的引用存储在"that"中。这是因为"This"引用根据作用域而变化。在dblClick函数中,我需要引用对象中的rectangle值,因此我使用存储的引用。rect and that.angle

请看这个例子(从一个稍微可疑的上一个实例更新)

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

希望有帮助,

尼克


附录:Dan,如果你真的被这个卡住了,并且可以没有Raphael2给你的一些东西,我建议你回到Raphael 1.5.x。转换刚刚被添加到Raphael2中,旋转/平移/缩放代码在1.5.2中是完全不同的(并且更容易)。

看看我,更新我的帖子,希望报应…

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

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

我们可以看到这个例子中有一个矩形在rumble中旋转和移动。

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

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

一个注意事项:由于getBBox(false)返回的对象是对象的CORNER坐标(x, y),您需要通过执行…来计算重新绘制对象的中心。X = box[' X '] + (box['width']/2);Y = box[' Y '] + (box['height']/2);

,box = shapeObj。getBBox(false);

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

最新更新