2015-05-20 251 views
7

我想在从一个圆转换为圆角三角形时创建一个形状的动画。将一个CAShapeLayer圆动画成一个圆角三角形

TL; DR:如何在两个CGPath形状之间创建CAShapeLayerpath动画?我知道他们需要有相同数量的控制点,但我认为我正在这么做 - 这段代码有什么问题?

的开始和结束时的状态会是这个样子: transition http://clrk.it/4vJS+

这里是我试过到目前为止:我使用的是CAShapeLayer,和动画在其path属性的变化。

按照documentation(重点煤矿):

的路径对象可以使用任何CAPropertyAnimation的具体子类的动画。路径将作为“在线”点的线性混合插值; “离线”点可以非线性地内插(例如,以保持曲线导数的连续性)。 如果两个路径具有不同数量的控制点或分段,则结果未定义。

在试图得到圆形和三角形有相同数量的控制点,我让他们都四角形状:圆是一个重圆角矩形,三角形被“隐藏”一边是第四个控制点。

这里是我的代码:

self.view.backgroundColor = .blueColor() 

//CREATE A CASHAPELAYER 
let shape = CAShapeLayer() 
shape.frame = CGRect(x: 50, y: 50, width: 200, height: 200) 
shape.fillColor = UIColor.redColor().CGColor 
self.view.layer.addSublayer(shape) 

let bounds = shape.bounds 

//CREATE THE SQUIRCLE 
let squareRadius: CGFloat = CGRectGetWidth(shape.bounds)/2 

let topCorner = CGPointMake(bounds.midX, bounds.minY) 
let rightCorner = CGPointMake(bounds.maxX, bounds.midY) 
let bottomCorner = CGPointMake(bounds.midX, bounds.maxY) 
let leftCorner = CGPointMake(bounds.minX, bounds.midY) 

let squarePath = CGPathCreateMutable() 
let squareStartingPoint = midPoint(leftCorner, point2: topCorner) 
CGPathMoveToPoint(squarePath, nil, squareStartingPoint.x, squareStartingPoint.y) 
addArcToPoint(squarePath, aroundPoint: topCorner, onWayToPoint: rightCorner, radius: squareRadius) 
addArcToPoint(squarePath, aroundPoint: rightCorner, onWayToPoint: bottomCorner, radius: squareRadius) 
addArcToPoint(squarePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: squareRadius) 
addArcToPoint(squarePath, aroundPoint: leftCorner, onWayToPoint: topCorner, radius: squareRadius) 
CGPathCloseSubpath(squarePath) 
let square = UIBezierPath(CGPath: squarePath) 

//CREATE THE (FAKED) TRIANGLE 
let triangleRadius: CGFloat = 25.0 

let trianglePath = CGPathCreateMutable() 
let triangleStartingPoint = midPoint(topCorner, point2: rightCorner) 

let startingPoint = midPoint(topCorner, point2: leftCorner) 
CGPathMoveToPoint(trianglePath, nil, startingPoint.x, startingPoint.y) 
let cheatPoint = midPoint(topCorner, point2:bottomCorner) 
addArcToPoint(trianglePath, aroundPoint: topCorner, onWayToPoint: cheatPoint, radius: triangleRadius) 
addArcToPoint(trianglePath, aroundPoint: cheatPoint, onWayToPoint: bottomCorner, radius: triangleRadius) 
addArcToPoint(trianglePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: triangleRadius) 
addArcToPoint(trianglePath, aroundPoint: leftCorner, onWayToPoint: topCorner, radius: triangleRadius) 
CGPathCloseSubpath(trianglePath) 
let triangle = UIBezierPath(CGPath: trianglePath) 


shape.path = square.CGPath 

...,后来,在viewDidAppear

let animation = CABasicAnimation(keyPath: "path") 
animation.fromValue = self.square.CGPath 
animation.toValue = self.triangle.CGPath 
animation.duration = 3 
self.shape.path = self.triangle.CGPath 
self.shape.addAnimation(animation, forKey: "animationKey") 

我有两个小巧的功能使代码更易读:

func addArcToPoint(path: CGMutablePath!, aroundPoint: CGPoint, onWayToPoint: CGPoint, radius: CGFloat) { 
    CGPathAddArcToPoint(path, nil, aroundPoint.x, aroundPoint.y, onWayToPoint.x, onWayToPoint.y, radius) 
} 

func midPoint(point1: CGPoint, point2: CGPoint) -> CGPoint { 
    return CGPointMake((point1.x + point2.x)/2, (point1.y + point2.y)/2) 
} 

我目前的结果是这样的:

triangle-to-square

注:我试图打造出圆一个非常全面的广场,企图得到的矢量形状相同数量的控制点。在上面的GIF中,拐角半径已经减小,以使变形更明显。

你认为怎么回事?我可以怎样才能达到这个效果?

+0

尝试使用很多直线段代替两种形状? – nielsbot

+0

实际上,你确定你正在使用addArcToPoint()吗? – nielsbot

+0

(我不叮嘱*路径*参数addArcToPoint()是可选的) – nielsbot

回答

6

因为addArcToPoint()跳过创建一个线段,如果路径的当前点和它的第一个参数相等,您不会结束相同数量的控制点。

从它的文档(重点煤矿):

如果当前点和圆弧(起点)不相等的第一切点,石英追加从当前的直线段指向第一个切点。

在三角使代码这两个电话不会产生线段,而相应的电话拨打了方圆形会:

addArcToPoint(trianglePath, aroundPoint: cheatPoint, onWayToPoint: bottomCorner, radius: triangleRadius) 
addArcToPoint(trianglePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: triangleRadius) 

可以手动创建的线条,不过,通过调用CGPathAddLineToPoint 。你可以使用CGPathApplierFunction来检查你的工作,这可以让你列举控制点。


也~~~我可能会建议纺显示链接,写一个函数指定为每t ∈ [0, 1]所需的形状很可能将是一个更清晰,更易于维护的解决方案。

+0

非常感谢!是的,这将是一个交互式的循环 - 你将它拖动到左边,圆形变成了一个箭头,把它拖到右边,它就会以另一种方式 - 所以我一定要写这个功能! – bryanjclark

+1

Facebook的Pop框架是用曲线,链接等构建动画的一种非常好的方式,可以使用这些功能(请参阅https://github.com/facebook/pop/blob/master/pop/POPAnimatableProperty.h)。 –