flex 图片旋转(解决公转和自转问题)

在Flex中图片的旋转是既有公转和自转的。这样在图片旋转的时候就有一定小麻烦;

为了更好地说明问题,先引入两个概念:“自转”和“公转”。想象一下,地球在绕着太阳公转的同时,它自己也在自转。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,所以没有体现在图片上。

matrix rotate - cruelchen - 请重启您的计算........

 

场景2影片剪辑的注册点在左上角,影片剪辑在场景中的初始位置为(70,70)

结果:影片剪辑自转的同时绕着(0,0)公转,公转的半径为70

matrix rotate - cruelchen - 请重启您的计算........

 

场景3影片剪辑的注册点在中心,影片剪辑在场景中的初始位置为(0,0)

结果:影片剪辑自转的同时绕着(0,0)公转,因为公转的半径为0,所以没体现在图上。

matrix rotate - cruelchen - 请重启您的计算........

场景4影片剪辑的注册点在中心,影片剪辑在场景中的初始位置为(125,30)

结果:影片剪辑自转的同时绕着(0,0)公转,因为公转的半径为125

matrix rotate - cruelchen - 请重启您的计算........

 

在上面的这些场景中,第三个场景的影片剪辑是绕中心旋转的。也就是说,影片剪辑在满足以下两个条件时,才能使用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 - cruelchen - 请重启您的计算........

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

matrix rotate - cruelchen - 请重启您的计算........ 

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

 

matrix rotate - cruelchen - 请重启您的计算........

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

 

matrix rotate - cruelchen - 请重启您的计算........

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

 

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

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

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对象带来变形效果。因此,matrixtransformPoint方法能把本地坐标转换成父容器坐标。

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

解决方案就是把图片放到父容器的(0,0)点进行旋转,在平一会你想好要的位置;

 

public function draw( displayTarget : Sprite , point : Point , index : int ) : void
{
var graphics : Graphics = displayTarget.graphics ;	
var metrix : Matrix = new Matrix( 1.0 , 0.0 , 0.0 , 1.0 , 0 , 0 );
graphics.beginBitmapFill( this._image , metrix , false , true );
graphics.drawRect( 0,0, this.size, this.size);
graphics.endFill();

//求等腰三角形斜边长 的一办
var halfDiagonal : Number = (this.size/2)*Math.sqrt(2);
var rotateAngle : Number = 0;

displayTarget.rotation = angle;


if(0 <= angle && angle < 45)
{
rotateAngle = (angle+45)* (Math.PI/180); 

displayTarget.x = point.x - Math.cos(rotateAngle)*halfDiagonal;
displayTarget.y = point.y - Math.sin(rotateAngle)*halfDiagonal;

}else if(45 <= angle && angle < 135)
{
rotateAngle = (angle - 45)* (Math.PI/180);

displayTarget.x = point.x + (Math.sin(rotateAngle)*halfDiagonal);
displayTarget.y = point.y - (Math.cos(rotateAngle)*halfDiagonal);
} else if(135 <= angle && angle < 225)
{
rotateAngle = (angle - 135)* (Math.PI/180);

displayTarget.x = point.x + (Math.cos(rotateAngle)*halfDiagonal);
displayTarget.y = point.y + (Math.sin(rotateAngle)*halfDiagonal);
} else if(225 <= angle && angle < 315)
{
rotateAngle = (angle - 225)* (Math.PI/180);

displayTarget.x = point.x - (Math.sin(rotateAngle)*halfDiagonal);
displayTarget.y = point.y + (Math.cos(rotateAngle)*halfDiagonal);
} else if(315 <= angle && angle < 360)
{
rotateAngle = (angle - 315)* (Math.PI/180);

displayTarget.x = point.x - (Math.cos(rotateAngle)*halfDiagonal);
displayTarget.y = point.y - (Math.sin(rotateAngle)*halfDiagonal);
}

}

  

原文地址:https://www.cnblogs.com/youngKen/p/3430821.html