如何使物体绕任意点旋转?

发布于 2024-11-10 08:56:56 字数 982 浏览 0 评论 0原文

我想以圆形方式围绕任意点旋转 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];

}

I want to rotate an UILabel around an arbitrary point in a circular manner, not a straight line. This is my code.The final point is perfect but it goes through a straight line between the initial and the end points.

- (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];

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

妥活 2024-11-17 08:56:56

您应该设置自己的 anchorPoint

使用关键帧动画来实现真正的锚点更改是非常过分的。

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

请阅读图层几何和变换部分核心动画编程指南了解更多信息。 它通过图像详细介绍了这一点,以帮助您理解。


编辑:要记住的一件事

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


应用于您的代码

您称为“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];
}

You should set your own anchorPoint

Its very much overkill to use a keyframe animation for what really is a change of the anchor point.

The anchor point is the point where all transforms are applied from, the default anchor point is the center. By moving the anchor point to (0,0) you can instead make the layer rotate from the bottom most corner. By setting the anchor point to something where x or y is outside the range 0.0 - 1.0 you can have the layer rotate around a point that lies outside of its bounds.

Please read the section about Layer Geometry and Transforms in the Core Animation Programming Guide for more information. It goes through this in detail with images to help you understand.


EDIT: One thing to remember

The frame of your layer (which is also the frame of your view) is calculated using the position, bounds and anchor point. Changing the anchorPoint will change where your view appears on screen. You can counter this by re-setting the frame after changing the anchor point (this will set the position for you). Otherwise you can set the position to the point you are rotating to yourself. The documentation (linked to above) also mentions this.


Applied to you code

The point you called "transportPoint" should be updated to calculate the difference between the rotation point and the lower left corner of the label divided by the width and height.

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

I also made the rotation point an argument to your method. The full updated code is below:

#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];
}
埖埖迣鎅 2024-11-17 08:56:56

我决定发布我的解决方案作为答案。它工作得很好,接受它没有旧解决方案的曲线动画(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"]; 
}

I decided to post my solution as an answer. It works fine accept it doesn't have the old solutions's curve animations (UIViewAnimationCurveEaseOut), but I can sort that out.

#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"]; 
}
小耗子 2024-11-17 08:56:56

CAKeyframeAnimation is the right tool for this job. Most UIKit animations are between start and end points. The middle points are not considered. CAKeyframeAnimation allows you to define those middle points to provide a non-linear animation. You will have to provide the appropriate bezier path for your animation. You should look at this example and the one's provided in the Apple documentation to see how it works.

壹場煙雨 2024-11-17 08:56:56

平移、绕中心旋转、向后平移。

translate, rotate around center, translate back.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文