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,所以没有体现在图片上。
场景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.rotateAroundInternalPoint和MatrixTransformer.rotateAroundExternalPoint。这两个静态方法用起来可比matrix.rotate方便多了。不需要考虑注册点的问题,也不需要把目标影片剪辑放到空的影片剪辑中。只可惜在Flex中没有这个类。
如何在Flex中实现旋转
在Flex环境中没有影片剪辑,它使用自己的一套组件,注册点默认在左上角,不能调整注册点的位置。我们如何实现组件的旋转呢?
1) 使用rotation属性。但是因不能调整组件的注册点,所以只能让组件绕左上角旋转。
2) 使用spark.effects.Rotate类。可以通过设置autoCenterTransform为true或false来控制组件是绕中心点旋转还是绕左上角旋转。
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),也就是图形中心点在父容器坐标系统中的新坐标。
解决方案就是把图片放到父容器的(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); } }
出处: http://youngken.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
![](https://img2024.cnblogs.com/blog/35695/202408/35695-20240826070647288-243310440.jpg)