iOS:CAShapeLayer路径转换

标签 ios core-graphics cashapelayer

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

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

最佳答案

通过仔细选择您的控制点等,您可能会设计出一条绘制为三角形的路径,但它具有与您要绘制的圆相同数量的线段和控制点。像这样(所有数值都假设为iPhone屏幕,但重点是通用的):

@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];
    });

}

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

更一般地说,期望 CA 以某种在外观上令人满意的特定方式在两条任意路径之间神奇地变形是不合理的。即使努力手动构建这些路径,使它们可以进行变形,它们仍然看起来不像我认为的那样。

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

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

关于iOS:CAShapeLayer路径转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17858770/

相关文章:

ios - 对卡在终点的自定义按钮进行动画处理

iphone - CGPath 中的 CGGradient

swift - 如何删除进度条圆圈中的 CAShapeLayer

ios - CAShapeLayer路径 Spring 动画不是 'overshooting'

ios - 如何以编程方式获取颜色资源目录中包含的所有颜色?

ios - 由于所谓的 UISearchDisplayController,TestFlight 安装的应用程序崩溃

cocoa - NSBezierPath 到 NSImage 以避免 CoreAnimation

ios - CGPathAddArc 是否包含圆的中心像素?

ios - 在长按事件上画一个圆圈

iphone - 当满足特定条件时,如何删除在运行时创建的对象?