如何根据光标位置检测旋转元素的元素转换类型(缩放、移动、旋转)?
情况
我编写了以下方法(在 ActionScript 3 中,它是一个 Flash 应用程序),它返回当前鼠标位置相对于给定元素位置所需的转换类型。
元素可以移动、缩放和旋转。如果这些转换中的任何一个适用于给定的坐标,则此方法返回:(
担心,您不需要调试此方法,它有效 - 只是将其添加到此处以便您了解我在做什么)
//returns whether the mouse is at a transforming position
private static function mouseAtTransformPosition(cX:Number, cY:Number, eX:Number, eY:Number, eW:Number, eH:Number, margin:Number):String
{
//initialize the transformation type
var transformType:String = null;
//set the transformation type depending on the given coordinates
if ((cX > eX) && (cX < eX + eW) && (cY > eY) && (cY < eY + eH))
{
transformType = "mover";
}
else if ((cX > eX) && (cX < eX + eW) && (cY <= eY) && (cY >= eY - margin))
{
transformType = "scalerUp";
}
else if ((cX >= eX + eW) && (cX <= eX + eW + margin) && (cY > eY) && (cY < eY + eH))
{
transformType = "scalerRight";
}
else if ((cX > eX) && (cX < eX + eW) && (cY >= eY + eH) && (cY <= eY + eH + margin))
{
transformType = "scalerDown";
}
else if ((cX <= eX) && (cX >= eX - margin) && (cY > eY) && (cY < eY + eH))
{
transformType = "scalerLeft";
}
else if ((cX >= eX - margin) && (cX <= eX) && (cY >= eY - margin) && (cY <= eY))
{
transformType = "scalerUpLeft";
}
else if ((cX >= eX + eW) && (eX <= eX + eW + margin) && (cY >= eY - margin) && (cY <= eY))
{
transformType = "scalerUpRight";
}
else if ((cX >= eX + eW) && (cX <= eX + eW + margin) && (cY >= eY + eH) && (cY <= eY + eH + margin))
{
transformType = "scalerDownRight";
}
else if ((cX >= eX - margin) && (cX <= eX) && (cY >= eY + eH) && (cY <= eY + eH + margin))
{
transformType = "scalerDownLeft";
}
else if ((cX >= eX - margin * 2) && (cX <= eX) && (cY >= eY - margin * 2) && (cY <= eY))
{
transformType = "rotatorTopLeft";
}
else if ((cX >= eX + eW) && (cX <= eX + eW + margin * 2) && (cY >= eY - margin * 2) && (cY <= eY))
{
transformType = "rotatorTopRight";
}
else if ((cX >= eX + eW) && (cX <= eX + eW + margin * 2) && (cY >= eY + eH) && (cY <= eY + eH + margin * 2))
{
transformType = "rotatorBottomRight";
}
else if ((cX >= eX - margin * 2) && (cX <= eX) && (cY >= eY + eH) && (cY <= eY + eH + margin * 2))
{
transformType = "rotatorBottomLeft";
}
//return the found transformation type
return transformType;
}
不用 所需的所有参数:
cX: cursor X position
cY: cursor Y position
eX: element X position
eY: element Y position
eW: element width
eH: element height
margin: how far the cursor can be removed from an exact transformation location to still be applicable
如果没有适用的转换,则返回 null。
问题
现在,我的功能似乎运行良好。现在问题出现了,我正在向需要转换的元素引入旋转。
旋转元素时,角会移动到不同的位置。光标现在必须位于不同的位置才能根据情况获得相同的转换类型,就像我上面的方法一样(如果没有旋转):
cursor is...
inside the element -> move
at top of element -> scale up
at right of element -> scale right
at bottom of element -> scale down
at left of element -> scale left
at top left of element -> scale up-left
at top right of element -> scale up-right
at bottom left of element -> scale down-left
at bottom right of element -> scale down-right
further top left of element -> rotate right/left
further top right of element -> rotate right/left
further bottom right of element -> rotate right/left
further bottom left of element -> rotate right/left
问题
我的方法完美地做到了这一点,如果没有旋转。但是,如何更改此函数以考虑旋转,例如角坐标的变化?
Justin 的更新
//checks whether mouse transforming is applicable
private static function checkMouseTransforming(cX:Number, cY:Number, render:Sprite):void
{
//temp disable rotation to get accurate width/height, x and y
var realRotation:Number = render.getChildAt(0).rotation;
render.getChildAt(0).rotation = 0;
var eX:Number = render.x;
var eY:Number = render.y;
var eW:Number = render.width;
var eH:Number = render.height;
var margin:uint = 10;
//restore real rotation
render.getChildAt(0).rotation = realRotation;
cX -= eX + eW / 2;
cY -= eY + eH / 2;
var theta:Number = render.getChildAt(0).rotation * Math.PI / 180;
var newcX:Number = Math.cos(theta) * cX - Math.sin(theta) * cY;
var newcY:Number = Math.sin(theta) * cX + Math.cos(theta) * cY;
newcX += eX + eW / 2;
newcY += eY + eH / 2;
var transformType:String = mouseAtTransformPosition(newcX, newcY, eX, eY, eW, eH, margin);
if (transformType)
{
//...
}
如您所见,旋转是从渲染对象的子对象设置和接收的。这是因为实际的对象是一个positionWrapper,并且在这个包装器内部有一个rotationWrapper(旋转被设置为)并且在该包装器内部保留了实际的可见元素。位置包装器的 x、y、宽度和高度始终等于可见显示对象的 x、y、宽度和高度,因此这不会导致问题。
现在,结果如下:
- 没有旋转,一切都按预期工作。
- 旋转大约 45 度后,当我将鼠标悬停在视觉显示对象上时:
- 底部中心:它返回“scalerLeft”,而应该返回“scalerDown”
- 右中心:它返回scalerDown,而它应该返回scalerRight
- 顶部中心:它返回scalerRight,而它应该返回scalerUp
- 左中心:它返回scalerUp,而它应该返回scalerLeft(注意这里的偏移模式)
- 搬运工似乎返回得相当准确
- 旋转器似乎返回到拐角的直接底部,而不是应有的位置
- bottomLeft 缩放器似乎返回到右下角,我认为同样的偏移量就是问题所在,并且对于所有其他缩放器也可能是相同的
这是一个需要调试的错误,但也许这会有所帮助。
贾斯汀的更新#2 替代文本 http://feedpostal.com/transformBug.gif 旋转越高,偏移/错误似乎越强。当0旋转时,这个bug不会出现。
这对你或其他人有意义吗?提前致谢。
The situation
I've written the following method (in actionscript 3, it's a flash application) which returns what transformation type is required for the current mouse position in relation to a given element position.
An element can be moved, scaled and rotated. This method returns if any of these transformations are applicable with the given coordinates:
(don't worry, you don't need to debug this method, it works - just added it here for you to understand what I am doing)
//returns whether the mouse is at a transforming position
private static function mouseAtTransformPosition(cX:Number, cY:Number, eX:Number, eY:Number, eW:Number, eH:Number, margin:Number):String
{
//initialize the transformation type
var transformType:String = null;
//set the transformation type depending on the given coordinates
if ((cX > eX) && (cX < eX + eW) && (cY > eY) && (cY < eY + eH))
{
transformType = "mover";
}
else if ((cX > eX) && (cX < eX + eW) && (cY <= eY) && (cY >= eY - margin))
{
transformType = "scalerUp";
}
else if ((cX >= eX + eW) && (cX <= eX + eW + margin) && (cY > eY) && (cY < eY + eH))
{
transformType = "scalerRight";
}
else if ((cX > eX) && (cX < eX + eW) && (cY >= eY + eH) && (cY <= eY + eH + margin))
{
transformType = "scalerDown";
}
else if ((cX <= eX) && (cX >= eX - margin) && (cY > eY) && (cY < eY + eH))
{
transformType = "scalerLeft";
}
else if ((cX >= eX - margin) && (cX <= eX) && (cY >= eY - margin) && (cY <= eY))
{
transformType = "scalerUpLeft";
}
else if ((cX >= eX + eW) && (eX <= eX + eW + margin) && (cY >= eY - margin) && (cY <= eY))
{
transformType = "scalerUpRight";
}
else if ((cX >= eX + eW) && (cX <= eX + eW + margin) && (cY >= eY + eH) && (cY <= eY + eH + margin))
{
transformType = "scalerDownRight";
}
else if ((cX >= eX - margin) && (cX <= eX) && (cY >= eY + eH) && (cY <= eY + eH + margin))
{
transformType = "scalerDownLeft";
}
else if ((cX >= eX - margin * 2) && (cX <= eX) && (cY >= eY - margin * 2) && (cY <= eY))
{
transformType = "rotatorTopLeft";
}
else if ((cX >= eX + eW) && (cX <= eX + eW + margin * 2) && (cY >= eY - margin * 2) && (cY <= eY))
{
transformType = "rotatorTopRight";
}
else if ((cX >= eX + eW) && (cX <= eX + eW + margin * 2) && (cY >= eY + eH) && (cY <= eY + eH + margin * 2))
{
transformType = "rotatorBottomRight";
}
else if ((cX >= eX - margin * 2) && (cX <= eX) && (cY >= eY + eH) && (cY <= eY + eH + margin * 2))
{
transformType = "rotatorBottomLeft";
}
//return the found transformation type
return transformType;
}
An elaboration of all the parameters required:
cX: cursor X position
cY: cursor Y position
eX: element X position
eY: element Y position
eW: element width
eH: element height
margin: how far the cursor can be removed from an exact transformation location to still be applicable
If no transformation is applicable, it will return null.
The problem
Now, my function seems to work great. The problem arises now that I am introducing rotation to the elements that need to be transformed.
When you rotate an element, the corners move to a different position. The cursor now has to be at different positions to get the same transforming types depending on the situation, just like my above method does (if there is no rotation):
cursor is...
inside the element -> move
at top of element -> scale up
at right of element -> scale right
at bottom of element -> scale down
at left of element -> scale left
at top left of element -> scale up-left
at top right of element -> scale up-right
at bottom left of element -> scale down-left
at bottom right of element -> scale down-right
further top left of element -> rotate right/left
further top right of element -> rotate right/left
further bottom right of element -> rotate right/left
further bottom left of element -> rotate right/left
The question
My method does this perfectly, if there is no rotation. However, how do I alter this function to take rotation into consideration, for example the change of coordinates of the corners?
Update for Justin
//checks whether mouse transforming is applicable
private static function checkMouseTransforming(cX:Number, cY:Number, render:Sprite):void
{
//temp disable rotation to get accurate width/height, x and y
var realRotation:Number = render.getChildAt(0).rotation;
render.getChildAt(0).rotation = 0;
var eX:Number = render.x;
var eY:Number = render.y;
var eW:Number = render.width;
var eH:Number = render.height;
var margin:uint = 10;
//restore real rotation
render.getChildAt(0).rotation = realRotation;
cX -= eX + eW / 2;
cY -= eY + eH / 2;
var theta:Number = render.getChildAt(0).rotation * Math.PI / 180;
var newcX:Number = Math.cos(theta) * cX - Math.sin(theta) * cY;
var newcY:Number = Math.sin(theta) * cX + Math.cos(theta) * cY;
newcX += eX + eW / 2;
newcY += eY + eH / 2;
var transformType:String = mouseAtTransformPosition(newcX, newcY, eX, eY, eW, eH, margin);
if (transformType)
{
//...
}
As you can see, the rotation is set and received from the child of the rendered object. This is because the actual object is a positionWrapper and inside this wrapper there's a rotationWrapper (which the rotation is set to) and inside that wrapper the actual visible element remains. The x, y, width and height of the position wrapper are always equal to those of the visible display object so this shouldn't cause problems.
Now, these are the results:
- Without rotation, everything works as expected
- With about 45 rotation, when I mouse-over at visual display object's:
- bottom center: it returns "scalerLeft", while it should return "scalerDown"
- right center: it returns scalerDown, while it should return scalerRight
- top center: it returns scalerRight, while it should return scalerUp
- left center: it returns scalerUp, while it should return scalerLeft (notice the offset pattern here)
- the mover seems to return pretty accurately
- the rotater seems to return at the direct bottom of a corner, not where it should
- the bottomLeft scaler seems to return at the bottom right corner, I think this same offset is the problem and probably is the same for all other scalers too
This is a bugger to debug, but maybe this will help.
Update #2 for Justin
alt text http://feedpostal.com/transformBug.gif
The offset/bug seems to get stronger the higher the rotation is. With 0 rotation, this bug does not appear.
Does this make sense to you or anyone else? Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在这里做了一些假设。我假设该对象是矩形且二维的(无 3D 旋转)。我假设这是唯一可以通过转换来简化事物的对象。另外,从另一个答案的评论中,我了解到对象的尺寸(eX,eY,eW,eH)在旋转后仍然被列为相同。
现在,这是我的解决方案。您肯定需要知道物体的旋转角度(我假设顺时针旋转为正),我将其称为 theta。起初我认为您应该旋转各个区域的边界框(例如变换为“移动器”的区域),但这要复杂得多。您应该做的是围绕对象中心并沿 theta 的相反方向旋转鼠标的 x 和 y 坐标。以下是一些明确的步骤:
从相应的 x 和 y 坐标(cX 和 cY)中减去对象中心的 x 位移和 y 位移。结果值给出了远离对象中心的 x 和 y 位移。
使用角度 theta 逆时针旋转结果坐标。
将对象中心的 x 位移和 y 位移添加到上一步生成的 x 和 y 坐标中。
使用生成的 x 和 y 值作为 mouseAtTransformPosition 函数中的 cX 和 cY 值。
使用
现在这里有一些代码来说明这一点。
newcX 和 newcY 将是 mouseAtTransformPosition 中实际使用的坐标。
如果这没有意义或者您有任何疑问,请告诉我。
*** 更新:
我认为您旋转坐标的方式错误。要改变旋转方向,请使用:
而不是我上面的。试试这个并告诉我效果如何。
I've made a few assumptions here. I've assumed that the object is rectangular and two-dimensional (no 3D rotations). I have assumed that this is the only object that can be transformed to simplify things. Also, from the comments on the other answer, I understand that the dimensions of the object (eX, eY, eW, eH) are still listed as being the same after the rotation.
Now, here's my solution. You will definitely need to know the rotational angle (which I'll assume is positive for clockwise rotation) of the object which I will call theta. At first I thought that you should rotate the bounding boxes for the various areas (the area where the transformation is 'mover' for instance), but that is much more complicated. What you should do is rotate the x and y coordinates of the mouse around the center of object and in the opposite direction of theta. Here are some explicit steps:
Subtract the x-displacement and y-displacement of the object's center from the respective x- and y-coordinates (cX and cY). The resulting values give the x and y displacements away from the center of the object.
Rotate anticlockwise the resulting coordinates using the angle theta.
Add the x-displacement and y-displacement of the object's center to the resultant x and y coordinates from the previous step.
Use the resultant x and y values as the cX and cY values in your mouseAtTransformPosition function.
Now here's some code to illustrate this.
newcX and newcY will the coordinates actually used in mouseAtTransformPosition.
Let me know if this doesn't make sense or if you have any questions.
*** Updated:
I think that you are rotating the coordinates the wrong way. To change the direction of rotation use:
instead of what I have above. Try this and tell me how it goes.
如果您使用要转换的对象的
mouseX
/mouseY
属性而不是其父级(或就此而言的舞台)的属性,您将在对象坐标中获得它们空间。因此,您永远不必担心旋转或缩放,并且可以保持代码几乎不变。
我还建议使用
scaleX
/scaleY
来更改大小,因为width
和height
会随着旋转而改变物体。If you use the
mouseX
/mouseY
properties of the object you are transforming instead of those of it's parent (or the stage for that matter) you will get them in the objects coordinate space.Thus, you will never have to worry about rotation or scaling, and can keep your code pretty much as it is.
I would also recommend using
scaleX
/scaleY
to alter the size, sincewidth
andheight
will change as you rotate the object.