iOS:CAShape层path转换

在对path进行基本转换之前,我已经使用了CAShape Layer从一个较小的圆圈到一个更大的圆圈。 够好的,但是我试图把一个三angular形变成一个圆圈。 它工作,但转换是奇怪的。 换句话说,从一个形状到另一个形状,它在最终形状形成之前“翻转”,“扭曲”。 形状相同,从圆形到圆形都没有问题,但形状各异,变形怪异。

我想知道这是否是预期的方式? 还是有其他的技巧或解决方法,我们可以顺利地,并成比例地使用CAShape Layer将一个形状转换成另一个不同的形状(或者是否有其他方式来做到这一点,它不一定是CAShape Layer )? 提前致谢。

通过仔细select你的控制点等,你可能会做一个三angular形绘制的path,但它有相同数量的段和控制点的圆你想画。 像这样(所有的数值都假定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((ax + bx) * 0.5f, (ay + by) * 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]; }); } 

即使段数和控制点数量相同,animation仍然看起来有些不可思议,可能不是你想要的。 其原因是CA似乎将所有的段和控制点一一对应起来,然后在它们之间对所有点进行线性插值。 因为控制点与所得到的path之间的关系不是线性的(在这种情况下是三次方),线性插入控制点位置不会导致path以线性方式移动。 如果你运行这个代码,你可以看到,当它转换的时候,在三angular形的边上有弧形的小圆点,其中有圆弧的其他点。

更一般地说,期望CA以某种特定的方式在两条任意path之间奇迹般地变形,这在外观上是可取的是不合理的。 即使要努力构build这些path,以便他们可能有变形的祈祷,他们仍然没有看起来像我认为他们应该。

通过使用平坦path(即由许多小直线代替曲线path元素组成的path)来达到所需的效果可能是更合理的。 即使这似乎是不平凡的,因为你会再次需要两个path具有相同数量的段,并且你将不得不构build这些段,使得公共点是整个段的正确数量path。

总之:这是一个相当复杂的问题,CoreAnimation提供的天真/免费解决scheme不太可能是你想要的。

CAShapeLayer path属性的文档:

如果两个path的控制点或段数不同,则结果不确定。

所以是的,我会说这是预料之中的,因为一个圆和一个三angular形是不同的形状。

本文详细介绍了自定义pathanimation。

我在这里猜测! 你可以使用3个点来定义一个圆。 这样你将有三angular形相同数量的点。 要定义这个圈子,这将是你最好的朋友

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