iOS:CAShape层路径转换

iOS:CAShape层路径转换,ios,core-graphics,cashapelayer,Ios,Core Graphics,Cashapelayer,在对路径进行基本转换之前,我使用了CAShape层——从一个较小的圆到一个较大的圆。很好,但是我试着把一个三角形变成一个圆;它起作用了,但这种转变很奇怪。换句话说,从一个形状到另一个形状,在形成最终形状之前,它会“翻转”、“扭曲”。对于相同的形状,从一个圆到一个圆是没有问题的,但是对于不同的形状,转换是奇怪的 我想知道这是否是预期的方式?或者是否有其他技巧或解决方法,我们可以使用CAShape层(或者是否有其他方法;不一定是CAShape层)将一个形状平滑、成比例地转换为另一个不同的形状?提前谢

在对路径进行基本转换之前,我使用了
CAShape层
——从一个较小的圆到一个较大的圆。很好,但是我试着把一个三角形变成一个圆;它起作用了,但这种转变很奇怪。换句话说,从一个形状到另一个形状,在形成最终形状之前,它会“翻转”、“扭曲”。对于相同的形状,从一个圆到一个圆是没有问题的,但是对于不同的形状,转换是奇怪的


我想知道这是否是预期的方式?或者是否有其他技巧或解决方法,我们可以使用
CAShape层
(或者是否有其他方法;不一定是
CAShape层
)将一个形状平滑、成比例地转换为另一个不同的形状?提前谢谢

CAShapeLayer
上的
path
属性文档中:

如果两条路径具有不同数量的控制点或段,则结果未定义

是的,我想说这是意料之中的,因为一个圆和一个三角形是不同的形状


详细描述自定义路径动画。

我在这里猜!可以使用3个点定义圆。这样,您将拥有与三角形相同的点数。要定义圆圈,这将是你最好的朋友

+ (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

通过仔细选择控制点等,您可能可以制作一条绘制为三角形的路径,但其线段和控制点的数量与您要绘制的圆的数量相同。如下所示(所有数值均假定为iPhone屏幕,但重点是一般性的):

即使分段和控制点的数量相同,动画看起来仍然有点不稳定,可能不是您想要的。原因是CA似乎将所有线段和控制点一一匹配,然后对所有点在它们之间进行线性插值。由于控制点和生成的路径之间的关系不是线性的(在本例中为立方),因此线性插值控制点位置不会导致路径以线性方式移动。如果你运行这段代码,你可以看到当它转换时,在三角形的边上有一些奇怪的凸起,圆的路径在圆弧上有其他的点

更一般地说,期望CA以某种特定的方式在两条任意路径之间神奇地变形是不合理的,这在外观上是可取的。即使去努力手工建造这些路径,以便他们可以进行变形祈祷,他们仍然没有看起来像我认为他们应该的那样

通过使用平坦路径(即由许多小直线组成的路径,而不是弯曲路径元素)来实现所需效果可能更为合理。即使这看起来也很重要,因为您同样需要两条路径具有相同数量的段,并且您必须构造这些段,以便公共点是整个路径上正确数量的段


总之:这是一个相当复杂的问题,CoreAnimation提供的简单/免费解决方案不太可能满足您的需求。

@DavidRönnqvist Ah。。你说bezierPathWithArcCenter方法可能会使用引擎盖下的控制点?我没想过!我更多的是指文档中所说的:“如果两条路径有不同数量的控制点或线段,则结果是未定义的。”3条直线的线段数与3条圆弧的线段数相同,但它们可能没有相同数量的控制点。@DavidRönnqvist Yep,你是对的。我对Unheilig的发现很好奇。也许有用?虽然在这种情况下文档不能保证任何东西:)@Unheilig当然可以。他们之间有免费的桥梁。你试过了吗?我对结果很好奇:)+1很好,你做了我想做的工作,但从来没有精力去弄清楚:)@ipmcc:很好。后来我也有同样的想法。我会接受你们的答案。如果你们中有人能进一步完善这个,请回来分享。
@implementation ViewController
{
    CAShapeLayer* shapeLayer;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    shapeLayer = [[CAShapeLayer alloc] init];

    CGRect bounds = self.view.bounds;
    bounds.origin.x += 0.25 * bounds.size.width;
    bounds.size.width *= 0.5;
    bounds.origin.y += 0.25 * bounds.size.height;
    bounds.size.height *= 0.5;

    shapeLayer.frame = bounds;
    shapeLayer.backgroundColor = [[UIColor redColor] CGColor];

    [self.view.layer addSublayer: shapeLayer];
    [self toCircle: nil];
}

CGPoint AveragePoints(CGPoint a, CGPoint b)
{
    return CGPointMake((a.x + b.x) * 0.5f, (a.y + b.y) * 0.5f);
}

- (IBAction)toCircle:(id)sender
{
    UIBezierPath* p = [[UIBezierPath alloc] init];

    [p moveToPoint: CGPointMake(80, 56)];
    [p addCurveToPoint:CGPointMake(144, 120) controlPoint1:CGPointMake(115.34622, 56) controlPoint2:CGPointMake(144, 84.653778)];
    [p addCurveToPoint:CGPointMake(135.42563, 152) controlPoint1:CGPointMake(144, 131.23434) controlPoint2:CGPointMake(141.0428, 142.27077)];
    [p addCurveToPoint:CGPointMake(48, 175.42563) controlPoint1:CGPointMake(117.75252, 182.61073) controlPoint2:CGPointMake(78.610725, 193.09874)];
    [p addCurveToPoint:CGPointMake(24.574375, 152) controlPoint1:CGPointMake(38.270771, 169.80846) controlPoint2:CGPointMake(30.191547, 161.72923)];
    [p addCurveToPoint:CGPointMake(47.999996, 64.574379) controlPoint1:CGPointMake(6.9012618, 121.38927) controlPoint2:CGPointMake(17.389269, 82.24749)];
    [p addCurveToPoint:CGPointMake(80, 56) controlPoint1:CGPointMake(57.729225, 58.957207) controlPoint2:CGPointMake(68.765656, 56)];
    [p closePath];

    [CATransaction begin];
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    pathAnimation.duration = 3.f;
    pathAnimation.fromValue = (id)shapeLayer.path;
    pathAnimation.toValue = (id)p.CGPath;
    [shapeLayer addAnimation:pathAnimation forKey:@"path"];
    [CATransaction setCompletionBlock:^{
        shapeLayer.path = p.CGPath;
    }];
    [CATransaction commit];

    double delayInSeconds = 4.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self toTriangle: nil];
    });

}

- (IBAction)toTriangle: (id)sender
{
    UIBezierPath* p = [[UIBezierPath alloc] init];

    // Triangle using the same number and kind of points...
    [p moveToPoint: CGPointMake(80, 56)];
    [p addCurveToPoint: AveragePoints(CGPointMake(80, 56), CGPointMake(135.42563, 152))  controlPoint1:CGPointMake(80, 56) controlPoint2:AveragePoints(CGPointMake(80, 56), CGPointMake(135.42563, 152))];
    [p addCurveToPoint:CGPointMake(135.42563, 152) controlPoint1:AveragePoints(CGPointMake(80, 56), CGPointMake(135.42563, 152)) controlPoint2:CGPointMake(135.42563, 152)];
    [p addCurveToPoint:AveragePoints(CGPointMake(135.42563, 152), CGPointMake(24.574375, 152)) controlPoint1:CGPointMake(135.42563, 152) controlPoint2:AveragePoints(CGPointMake(135.42563, 152), CGPointMake(24.574375, 152))];
    [p addCurveToPoint:CGPointMake(24.574375, 152) controlPoint1:AveragePoints(CGPointMake(135.42563, 152), CGPointMake(24.574375, 152)) controlPoint2:CGPointMake(24.574375, 152)];
    [p addCurveToPoint: AveragePoints(CGPointMake(24.574375, 152),CGPointMake(80, 56)) controlPoint1:CGPointMake(24.574375, 152) controlPoint2:AveragePoints(CGPointMake(24.574375, 152),CGPointMake(80, 56)) ];
    [p addCurveToPoint:CGPointMake(80, 56) controlPoint1:AveragePoints(CGPointMake(24.574375, 152),CGPointMake(80, 56)) controlPoint2:CGPointMake(80, 56)];
    [p closePath];

    [CATransaction begin];
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    pathAnimation.duration = 3.f;
    pathAnimation.fromValue = (id)shapeLayer.path;
    pathAnimation.toValue = (id)p.CGPath;
    [shapeLayer addAnimation:pathAnimation forKey:@"path"];
    [CATransaction setCompletionBlock:^{
        shapeLayer.path = p.CGPath;
    }];
    [CATransaction commit];

    double delayInSeconds = 4.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self toCircle: nil];
    });

}