[英]Animate a CAShapeLayer circle into a rounded-corner triangle
I'd like to animate a shape as it transitions from a circle to a rounded-corner triangle. 我想为一个形状设置动画,因为它从圆形过渡到圆角三角形。
TL;DR: How do I animate a CAShapeLayer
's path
between two CGPath
shapes? TL; DR:如何在两个
CGPath
形状之间设置CAShapeLayer
path
动画? I know that they need to have the same number of control points, but I think I'm doing that - what's wrong with this code? 我知道他们需要拥有相同数量的控制点,但我认为我这样做 - 这段代码出了什么问题?
The beginning and end states would look something like this: transition http://clrk.it/4vJS+ 开始和结束状态看起来像这样: 转换http://clrk.it/4vJS+
Here's what I've tried so far: I'm using a CAShapeLayer
, and animating a change in its path
property. 以下是我到目前为止所尝试的内容:我正在使用
CAShapeLayer
,并对其path
属性进行动画更改。
According to the documentation (emphasis mine): 根据文件 (强调我的):
The path object may be animated using any of the concrete subclasses of
CAPropertyAnimation
.可以使用
CAPropertyAnimation
任何具体子类对路径对象进行动画CAPropertyAnimation
。 Paths will interpolate as a linear blend of the "on-line" points;路径将插值为“在线”点的线性混合; "off-line" points may be interpolated non-linearly (eg to preserve continuity of the curve's derivative).
“离线”点可以非线性插值(例如,以保持曲线导数的连续性)。 If the two paths have a different number of control points or segments the results are undefined.
如果两个路径具有不同数量的控制点或段,则结果未定义。
In an attempt to get the circle and triangle to have the same number of control points, I made them both four-pointed shapes: the circle is a heavily-rounded rectangle, and the triangle is "hiding" a fourth control point on one side. 为了使圆和三角形具有相同数量的控制点,我将它们制成四角形:圆形是一个重圆角矩形,三角形“隐藏”一侧的第四个控制点。
Here's my code: 这是我的代码:
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
...and later on, in viewDidAppear
: ......以及稍后,在
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")
I have two quick little functions for making the code more legible: 我有两个快速的小函数,使代码更清晰:
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)
}
My current result looks like this: 我目前的结果如下:
NOTE: I'm trying to build the circle out of a very-rounded square, in an attempt to get the same number of control points in the vector shape. 注意:我试图从一个非常圆的方块构建圆,试图在矢量形状中获得相同数量的控制点。 In the above GIF, the corner radius has been reduced to make the transformation more visible.
在上面的GIF中,角半径已经减小,使转换更加明显。
What do you think is going on? 你觉得怎么样? How else might I achieve this effect?
我怎么能达到这个效果呢?
You're not ending up with the same number of control points because addArcToPoint()
skips creating a line segment if the path's current point and its first argument are equal. 你不会得到相同数量的控制点,因为如果路径的当前点和它的第一个参数相等,
addArcToPoint()
跳过创建一个线段。
From its documentation (emphasis mine): 从其文档(强调我的):
If the current point and the first tangent point of the arc (the starting point) are not equal , Quartz appends a straight line segment from the current point to the first tangent point.
如果当前点和弧的第一个切点(起点) 不相等 ,则Quartz会将当前点的直线段附加到第一个切点。
These two calls in your triangle-making code won't produce line segments, while the corresponding calls making the squircle will: 你的三角形代码中的这两个调用不会产生线段,而制作squircle的相应调用将:
addArcToPoint(trianglePath, aroundPoint: cheatPoint, onWayToPoint: bottomCorner, radius: triangleRadius)
addArcToPoint(trianglePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: triangleRadius)
You can manually create the lines, though, by calling CGPathAddLineToPoint
. 但是,您可以通过调用
CGPathAddLineToPoint
手动创建行。 And you can check your work using CGPathApplierFunction
, which will let you enumerate the control points. 您可以使用
CGPathApplierFunction
检查您的工作,它可以让您枚举控制点。
Also~~~ I might suggest that spinning a display link and writing a function which specifies the desired shape for every t ∈ [0, 1]
is probably going to be a clearer, more maintainable solution. 还~~~我可能会建议旋转显示链接并编写一个函数来指定每个
t ∈ [0, 1]
所需的形状,这可能是一个更清晰,更易维护的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.