2013-07-25 40 views
1

在對路徑進行基本轉換之前,我已經使用了CAShape Layer - 從較小的圓圈到較大的圓圈。足夠好,但是我試圖將一個三角形變成一個圓圈;它工作,但轉變​​是奇怪的。換言之,從一種形狀到另一種形狀,它在最終形狀形成之前「翻轉」,「扭曲」。具有相同的形狀,沒有問題 - 從一個圓圈到一個圓圈 - 但是具有不同的形狀,這種變換很奇怪。iOS:CAShape層路徑轉換

我想知道這是否是預期的方式?還是有其他的技巧或解決方法,我們可以使用CAShape Layer(或者是否有其他方法來做到這一點;它不一定是CAShape Layer)可以平滑並按比例將一個形狀轉換爲另一個形狀?提前致謝。

回答

9

通過仔細選擇你的控制點,等等,你也許可以編造其繪製成三角形的路徑,但具有相同的段數和控制點的作爲你想畫的圓圈。像這樣(所有數值假設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以某種特定方式在兩個任意路徑之間奇蹟般地變形,這在外觀上是可取的,這是不合理的。即使要努力構建這些路徑,以便他們可能有變形的祈禱,他們仍然沒有看起來像我認爲他們應該。

通過使用平坦路徑(即由許多小直線代替曲線路徑元素組成的路徑)來達到所需效果可能更合理。即使這似乎是不平凡的,因爲你會再次需要兩條路徑具有相同數量的分段,並且您將不得不構建這些分段,使得公共點是沿着整體的正確數量的分段路徑。

總結:這是一個相當複雜的問題,CoreAnimation提供的天真/免費解決方案不太可能是您想要的。

+0

+1不錯,你做了我雖然的工作,但永遠不會有能量找出:) –

+0

@ipmcc:非常好。之後,我也有同樣的想法。我會接受你的回答。 – Unheilig

+0

如果您有任何人可以以某種方式進一步細化,請回來分享。 – Unheilig

1

path屬性的文檔上CAShapeLayer

如果兩個路徑具有不同數量的控制點或線段的結果是不確定的。

所以是的,我會說這是預期的,因爲一個圓和一個三角形是不同的形狀。

This article非常詳細地描述了自定義路徑動畫。

1

我在這裏猜!您可以使用3個點來定義一個圓。這樣你就可以得到與三角形相同數量的點。要定義的圈子,這將是你最好的朋友

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

那些控制點呢? –

+0

@DavidRönnqvist啊..你說bezierPathWithArcCenter-方法可能會使用控制點?沒有想過那個! – hfossli

+0

我更多地指的是文檔的說明:「如果兩條路徑的**控制點**或段的數目不同,則結果未定義。」 3行是與3個弧相同數量的段,但它們*可能*不具有相同數量的控制點。 –