通过仔细选择控制点等,您可能可以绘制一条绘制为三角形的路径,但其线段和控制点的数量与您要绘制的圆形相同。像这样(所有数值均假设 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];
});
}
Even with段数和控制点的数量相同,动画still看起来有点奇怪,可能不是你想要的。原因是 CA 似乎将所有线段和控制点一对一匹配,然后对所有点在它们之间进行线性插值。由于控制点和生成的路径之间的关系不是线性的(在本例中为三次),因此线性插值控制点位置不会导致路径以线性方式移动。如果运行此代码,您可以看到,当它转换时,三角形的一侧有奇怪的驼峰,其中圆形路径在圆弧中具有其他点。
更一般地说,期望 CA 以某种特定的方式在两条任意路径之间神奇地变形(而事实证明这种方式在外观上是理想的)是不合理的。即使努力手工构建这些路径,以便它们可以祈祷变形,它们仍然不像我想象的那样。
通过使用扁平路径(即由许多小直线而不是弯曲路径元素组成的路径)来实现所需的效果可能更合理。即使这看起来也很重要,因为您再次需要两条路径具有相同数量的线段,并且您必须构造这些线段,以便公共点是整个路径上正确数量的线段小路。
总而言之:这是一个相当复杂的问题,CoreAnimation 提供的简单/免费的解决方案不太可能满足您的需求。