Flex 旋转非常详细的讲解

来源:互联网 发布:良辰好景知几何 编辑:程序博客网 时间:2024/05/23 20:48

 

转:http://blog.csdn.net/sjz168/article/details/6597623

 

 

自转与公转

为了更好地说明问题,先引入两个概念:“自转”和“公转”。想象一下,地球在绕着太阳公转的同时,它自己也在自转。Flash应用中的显示对象可以进行自身的自转,也可以绕着某个点公转,也可以两者同时进行。

实现旋转常用的方法

1)displayObject.rotation=degree; //实现显示对象的自转(flash|Flex

2)spark.effects.Rotate; //实现显示对象的自转(Flex

3)matrix.rotate; //同时实现显示对象的自转和公转(flash|Flex

本文重点介绍matrix.rotate的实现细节。

最近做的一个小项目需要实现“图片绕中心旋转”的效果。在网上搜索了相关的资料,在很多帖子中都推荐使用Matrix来实现。代码如下:

var matrix:Matrix = img.transform.matrix;

matrix.translate(-img.width/2,-img.height/2);

matrix.rotate(radian);

matrix.translate(+img.width/2,+img.height/2);

img.transform.matrix = matrix;

相信做过“图片绕中心旋转”效果的朋友都见过这段代码。实现原理就是先把图片的中点移动到左上角,然后进行旋转操作,最后再把图片移回来。看起来很好理解,但是这段代码在我的Flex应用中却不能正常工作。又回到网上搜索了很长时间,发现这段代码出现在很多论坛博客中,没有一个说这段代码有问题的。通过测试,最后才发现了问题所在:此段代码需要在特定的前提下才能正常使用。这个前提下面会细说。现在我们先来看matix.rotate这个方法究竟做了什么。


matix.rotate做了什么

Flash IDE中做了一系列试验:在场景中放置一个影片剪辑,影片剪辑的注册点分别在左上角和中心,影片剪辑的x,y分别在(0,0)(70,70)

试验的结论是:

matrix.rotate方法让目标对象同时进行“自转和公转”,旋转的实现跟“影片剪辑的注册点的位置”和“影片剪辑的初始位置”有很大的关系。详见下面的4个场景(灰色框为场景,黑色框为旋转目标):

场景1影片剪辑的注册点在左上角,影片剪辑在场景中的初始位置为(0,0)
结果:影片剪辑自转的同时绕着(0,0)公转。因为公转的半径为0,所以没有体现在图片上。

场景2影片剪辑的注册点在左上角,影片剪辑在场景中的初始位置为(70,70)
结果:影片剪辑自转的同时绕着(0,0)公转,公转的半径为70

场景3影片剪辑的注册点在中心,影片剪辑在场景中的初始位置为(0,0)
结果:影片剪辑自转的同时绕着(0,0)公转,因为公转的半径为0,所以没体现在图上。


场景4影片剪辑的注册点在中心,影片剪辑在场景中的初始位置为(125,30)
结果:影片剪辑自转的同时绕着(0,0)公转,因为公转的半径为125

在上面的这些场景中,第三个场景的影片剪辑是绕中心旋转的。也就是说,影片剪辑在满足以下两个条件时,才能使用matrix.rotate方法实现绕中心旋转。

1)影片剪辑的注册点在中心;

2)影片剪辑的中心点与场景(或父影片剪辑)(0,0)重叠。

再重新观察上面的那段代码:

var matrix:Matrix = img.transform.matrix;

matrix.translate(-img.width/2,-img.height/2);

matrix.rotate(radian);

matrix.translate(+img.width/2,+img.height/2);

img.transform.matrix = matrix;

代码中使用了translate方法,很显然目的就是为了让img的中心点与父影片剪辑的(0,0)点重叠。所以,使用这段代码的前提条件是:

1)Flash IDE中开发;(才能调整注册点的位置)

2)目标影片剪辑的注册点在中心;

3)把目标影片剪辑放在一个空影片剪辑中,并设置x,y(0,0)

善了个哉的,网上这么多篇文章提到这段代码,居然没有一篇提到上述的条件,坑爹呐!

如果是在Flash IDE环境中进行开发,实现旋转还有一个更方便的可选项,那就是MatrixTransformer.rotateAroundInternalPointMatrixTransformer.rotateAroundExternalPoint。这两个静态方法用起来可比matrix.rotate方便多了。不需要考虑注册点的问题,也不需要把目标影片剪辑放到空的影片剪辑中。只可惜在Flex中没有这个类。

如何在Flex中实现旋转

Flex环境中没有影片剪辑,它使用自己的一套组件,注册点默认在左上角,不能调整注册点的位置。我们如何实现组件的旋转呢?

1)使用rotation属性。但是因不能调整组件的注册点,所以只能让组件绕左上角旋转。

2)使用spark.effects.Rotate类。可以通过设置autoCenterTransformtruefalse来控制组件是绕中心点旋转还是绕左上角旋转。

3)使用matrix.rotate方法。通过控制组件的“自转”和“公转”来实现我们需要的旋转。

调用matrix.rotate方法,会使目标对象同时进行“自转”和“公转”,这可以从上面的场景2和场景4中明显看出。其实在场景1中和场景3中,目标对象也是同时进行了“自转”和“公转”,只是因为公转的半径为0,所以看不出来而已。

如果目标对象的初始位置不是(0,0)的话,如何实现目标对象的绕点旋转呢?首先,对于旋转目标的自转,我们不需要做什么。因为自转是必须的。而对于旋转目标的公转,我就需要做一些额外的操作了。因为公转改变了旋转目标的位置,这也是影响旋转目标是否绕点旋转的因素。调用matrix.rotate方法时,我们不能阻止旋转目标的公转,所以,解决办法只能是:在调用matrix.rotate之前,取得旋转中心点的位置,然后在旋转后,再取得旋转中心点的位置,然后计算点的位移,最后用matrix.translate方法将旋转目标调整到“正确位置”。

下面两组图是两个不同旋转中心下的旋转,旋转之后,我们再通过matrix.translate方法把旋转后的粉色的原点移动到它原始的状态及可。见图。


注册点在左上角,绕中心旋转。



注册点在左上角,绕左上角旋转。这种情况下,使用matrix.rotate方法显然是多余的,因为简单地使用displayObject.rotation属性就可以实现想要的旋转了。在这里我们使用matrix.rotate方法是为了更好地说明matrix.rotate方法的实现细节。


注册点在中心,绕中心点旋转。如果需要在Flash中用matrix.rotate方法实现旋转,且不依赖父影片剪辑,则使用该方法。


注册点在中心,绕左上角旋转。

最后提供一段代码,此代码实现让一个注册点在左上角的对象绕它的中心点旋转。代码如下:

//旋转之前获取旋转对象的中心点

var p1:Point = rotatedObject.localToGlobal(new Point(rotatedObject.width/2, rotatedObject.height/2));

//旋转

var matrix1:Matrix = rotatedObject.transform.matrix;

matrix1.rotate(radian);

rotatedObject.transform.matrix = matrix1;

//旋转后获取旋转对象的中心点

var p2:Point = rotatedObject.localToGlobal(new Point(rotatedObject.width/2, rotatedObject.height/2));

//位移

var matrix2:Matrix = rotatedObject.transform.matrix;

matrix2.translate(p1.x-p2.x,p1.y-p2.y);

rotatedObject.transform.matrix = matrix2;

2012-5-5补充:

更新一个新的旋转方式。这个方法是MatrixTransformer类中rotateAroundInternalPoint静态方法的做法。

假设有一个图片A,我们希望将图片绕O点旋转。O点在图片内部的坐标是(x,y)。我们可以先移动图片,让图片的O点与父容器的(0,0)点重合,调用rotate方法进行旋转,然后再将图片移动回正确的位置。代码如下:

var point:Point = new Point(x, y);

point = m.transformPoint(point);//将图片内部的点转换成父容器坐标的点

m.tx -= point.x;

m.ty -= point.y;

m.rotate(angleDegrees*(Math.PI/180));

m.tx += point.x;

m.ty += point.y;

MatrixTransformer类的另一个rotateAroundExternalPoint静态方法的做法类似,但是因为是以“外部的点”为旋转中心,所以代码有些不同:

var point:Point = new Point(x, y);

m.tx -= point.x;

m.ty -= point.y;

m.rotate(angleDegrees*(Math.PI/180));

m.tx += point.x;

m.ty += point.y;

在这个方法中,“外部的点”是在父容器的坐标空间中,所以不需要再转换该点了。

另一个旋转方式:

这个方法是某Transform tool中的做法。注意,这个方法跟上边补充的第一种方法不一样。

anchor = new Point(width/2, height/2); //旋转对象的坐标空间内旋转对象的中心点

// 把旋转对象的中心点转换成父容器坐标空间中的点

var globalAnchor:Point = matrix.transformPoint(anchor);  

// calculates position

var m:Matrix = new Matrix(); //创建了一个新的matrix对象

//下面的操作是把这个matrix对象转化成我们想要的

m.translate(-anchor.x, -anchor.y);//将目标的中心点移动到与父容器坐标系统的(0,0)重叠

m.rotate((rotation + angle)*Math.PI/180);//旋转目标

m.translate(globalAnchor.x, globalAnchor.y);//将目标的中心点移动到合适的位置

为什么var globalAnchor:Point = matrix.transformPoint(anchor);能把旋转对象的中心点的坐标转换成旋转对象的父容器的坐标呢?

当旋转对象放在父容器中,没有应用任何matrix变形时,它的坐标系统和父容器的坐标系统是重叠的,也就是说,旋转对象的中心点在父容器坐标系统中的坐标就是它的本地坐标。当旋转对象发生matrix变形后,旋转对象的中心点在父容器坐标系统的坐标发生了变化,而这个变化正是应用在旋转对象上的matrix对象带来变形效果。因此,matrix的transformPoint方法能把本地坐标转换成父容器坐标。

左上角的图形是变形前的,右边的图形是变形之后的。中心点1的本地坐标是(2,2),因为在没有使用任何matrix效果时,它的本地坐标系统跟父容器的坐标系统是重叠的。因此它在父容器中的坐标也是(2,2)。这个点在经过matrix变形后,就变成了(9,5),也就是图形中心点在父容器坐标系统中的新坐标。