如何围绕任意点旋转对象?

Posted

技术标签:

【中文标题】如何围绕任意点旋转对象?【英文标题】:How to rotate an object around a arbitrary point? 【发布时间】:2011-05-28 08:22:55 【问题描述】:

我想以圆形方式围绕任意点旋转 UILabel,而不是直线。这是我的代码。终点是完美的,但它通过起点和终点之间的直线。

- (void)rotateText:(UILabel *)label duration:(NSTimeInterval)duration degrees:(CGFloat)degrees 
    /* Setup the animation */
    [UILabel beginAnimations:nil context:NULL];
    [UILabel setAnimationDuration:duration];


    CGPoint rotationPoint = CGPointMake(160, 236);
    CGPoint transportPoint = CGPointMake(rotationPoint.x - label.center.x, rotationPoint.y - label.center.y);

    CGAffineTransform t1 = CGAffineTransformTranslate(label.transform, transportPoint.x, -transportPoint.y);
    CGAffineTransform t2 = CGAffineTransformRotate(label.transform,DEGREES_TO_RADIANS(degrees));
    CGAffineTransform t3 = CGAffineTransformTranslate(label.transform, -transportPoint.x, +transportPoint.y);

    CGAffineTransform t4 = CGAffineTransformConcat(CGAffineTransformConcat(t1, t2), t3);
    label.transform = t4;   

    /* Commit the changes */
    [UILabel commitAnimations];


【问题讨论】:

查看博文informit.com/blogs/… 【参考方案1】:

你应该设置你自己的anchorPoint

将关键帧动画用于真正的锚点变化是非常过分的。

锚点是应用所有变换的点,默认锚点是中心。通过将锚点移动到 (0,0),您可以使图层从最底角旋转。通过将锚点设置为 xy 超出范围 0.0 - 1.0 的位置,您可以让图层围绕位于其边界之外的点旋转。

请阅读核心动画编程指南中关于Layer Geometry and Transforms 的部分以获取更多信息。 它通过图像详细说明了这一点,以帮助您理解。


编辑:要记住的一件事

图层的框架(也是视图的框架)是使用位置、边界和锚点计算的。更改锚点将更改您的视图在屏幕上的显示位置。您可以通过在更改锚点后重新设置框架来解决此问题(这将为您设置位置)。否则,您可以将位置设置为您正在向自己旋转的点。 documentation (linked to above) 也提到了这一点。


适用于您的代码

应更新您称为“transportPoint”的点,以计算旋转点与标签左下角的差除以宽度和高度。

// Pseudocode for the correct anchor point
transportPoint = ( (rotationX - labelMinX)/labelWidth,
                   (rotationX - labelMinY)/labelHeight )

我还将旋转点作为您方法的参数。完整的更新代码如下:

#define DEGREES_TO_RADIANS(angle) (angle/180.0*M_PI)

- (void)rotateText:(UILabel *)label 
       aroundPoint:(CGPoint)rotationPoint 
          duration:(NSTimeInterval)duration 
           degrees:(CGFloat)degrees 

    /* Setup the animation */
    [UILabel beginAnimations:nil context:NULL];
    [UILabel setAnimationDuration:duration];

    // The anchor point is expressed in the unit coordinate
    // system ((0,0) to (1,1)) of the label. Therefore the 
    // x and y difference must be divided by the width and 
    // height of the label (divide x difference by width and 
    // y difference by height).
    CGPoint transportPoint = CGPointMake((rotationPoint.x - CGRectGetMinX(label.frame))/CGRectGetWidth(label.bounds),
                                         (rotationPoint.y - CGRectGetMinY(label.frame))/CGRectGetHeight(label.bounds));

    [label.layer setAnchorPoint:transportPoint];
    [label.layer setPosition:rotationPoint]; // change the position here to keep the frame 
    [label.layer setTransform:CATransform3DMakeRotation(DEGREES_TO_RADIANS(degrees), 0, 0, 1)];   

    /* Commit the changes */
    [UILabel commitAnimations];

【讨论】:

【参考方案2】:

我决定发布我的解决方案作为答案。它工作正常,接受它没有旧解决方案的曲线动画(UIViewAnimationCurveEaseOut),但我可以解决这个问题。

#define DEGREES_TO_RADIANS(angle) (angle / 180.0 * M_PI)

- (void)rotateText:(UILabel *)label duration:(NSTimeInterval)duration degrees:(CGFloat)degrees 
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddArc(path,nil, 160, 236, 100, DEGREES_TO_RADIANS(0), DEGREES_TO_RADIANS(degrees), YES);

    CAKeyframeAnimation *theAnimation;

    // animation object for the key path
    theAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    theAnimation.path=path;
    CGPathRelease(path);

    // set the animation properties
    theAnimation.duration=duration;
    theAnimation.removedOnCompletion = NO; 
    theAnimation.autoreverses = NO;
    theAnimation.rotationMode = kCAAnimationRotateAutoReverse;
    theAnimation.fillMode = kCAFillModeForwards;

    [label.layer addAnimation:theAnimation forKey:@"position"]; 

【讨论】:

+1:感谢您发布您的解决方案!这可能会在未来对其他人有所帮助。 这里也一样,谢谢你的解决方案,另一件事。你解决宽松了吗?我正在尝试做类似 UIScrollView 但循环的事情,任何进一步的帮助都会很棒。 最后我使用了其他东西,但是对于缓动,您可以连续创建一个主旋转和一个缓入缓出旋转。 在这种情况下使用关键帧过于复杂。看看我的答案,我为旋转设置了一个自定义锚点。它的工作原理与您问题中的代码非常相似,只是没有来回转换和连接多个转换。【参考方案3】:

CAKeyframeAnimation 是完成这项工作的正确工具。大多数 UIKit 动画都在起点和终点之间。不考虑中间点。 CAKeyframeAnimation 允许您定义这些中间点以提供非线性动画。您必须为动画提供适当的贝塞尔路径。您应该查看this example 和 Apple 文档中提供的那个,以了解它是如何工作的。

【讨论】:

感谢您的回答,我发现自己使用了苹果示例中的 CAKeyframeAnimation。 我会说设置锚点是围绕任意点旋转的工作。这就是锚点。使用关键帧动画过于复杂,并不是真正“适合这项工作”【参考方案4】:

平移,围绕中心旋转,向后平移。

【讨论】:

我认为这就是我正在做的事情,但它并没有通过循环路径。这是一条直线。

以上是关于如何围绕任意点旋转对象?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以围绕 SpriteKit 中的任意点旋转节点?

通过金属中的任意 (x,y) 点围绕 z 轴旋转

向量学习2:图形围绕自己中心旋转围绕图形外或内任意点为中心旋转

向量学习2:图形围绕自己中心旋转围绕图形外或内任意点为中心旋转

如何多次围绕锚点旋转视图

SceneKit:围绕任意点进行轨道运行而无需相机跳跃或将锚点移动到中心?