我使用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, "<>");
});
}
拖放正在工作,并且其中一个形状在双击时旋转。然而,有两个问题:
如何将旋转自动附加到每个形状上,而无需将每个项目引用硬编码到旋转方法中?也就是说,我只想画一次形状,然后让它们都自动暴露在相同的行为下,这样它们就可以独立地拖动/放置/旋转,而不必显式地将该行为应用于每个形状。
在一个形状被旋转后,它不再正确地拖动-好像鼠标拖动移动与形状的原始方向有关,而不是在形状被旋转时更新。我怎样才能让这个工作正确,使形状可以拖动和旋转许多次,无缝?
非常感谢任何提示!
我已经尝试了几次,把我的头围绕在新的转换引擎,但无济于事。所以,我回到了第一原则。
我终于成功地拖放了一个对象,经历了几次转换,试图找出不同的转换的影响- 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函数中,我需要引用对象中的rect和angle值,因此我使用存储的引用。rect and that.angle
请看这个例子(从一个稍微可疑的上一个实例更新)
可能有更好的方法来做你需要的,但这应该适合你。
希望有帮助,
尼克
附录:Dan,如果你真的被这个卡住了,并且可以没有Raphael2给你的一些东西,我建议你回到Raphael 1.5.x。转换刚刚被添加到Raphael2中,旋转/平移/缩放代码在1.5.2中是完全不同的(并且更容易)。
看看我,更新我的帖子,希望报应…
如果您不想使用ElbertF库,您可以将笛卡尔坐标转换为极坐标。
在必须添加或删除角度并在笛卡尔坐标中再次转换之后。
我们可以看到这个例子中有一个矩形在rumble中旋转和移动。