如何使 UIBezierPath 中的两条线以不同的速度进行动画处理
Posted
技术标签:
【中文标题】如何使 UIBezierPath 中的两条线以不同的速度进行动画处理【英文标题】:How to make two lines in UIBezierPath to animate at different speed 【发布时间】:2015-01-09 00:07:27 【问题描述】:说出以下场景:
我画了一个四边形,它是一个 UIView 的蒙版。我将形状层表示为maskLayer
。 maskLayer
不对称裁剪 UIView 的底部。
但是我想在动画中完全展示我的 UIView。动画应该是 maskLayer 的左侧下降到 UIView 的底部,0.2 秒后我的 maskLayer 的右侧也下降到 UIView 的底部,从而完全显示 UIView 的实体。
我的做法是先下拉左行,再下拉右行,如下代码:
//this quadrilateral will put down left corner to the bottom of screen
var path2 = UIBezierPath()
path2.moveToPoint(CGPointZero)
path2.addLineToPoint(CGPoint(x: 0, y: frame.height))
path2.addLineToPoint(CGPoint(x: frame.width, y: frame.height / goldRatio / goldRatio))
path2.addLineToPoint(CGPoint(x: frame.width, y: 0))
path2.closePath()
//this rectangle path will put down both corner to the bottom of screen
//thus fix the view to its original shape
var path3 = UIBezierPath()
path3.moveToPoint(CGPointZero)
path3.addLineToPoint(CGPoint(x: 0, y: frame.height))
path3.addLineToPoint(CGPoint(x: frame.width, y: frame.height))
path3.addLineToPoint(CGPoint(x: frame.width, y: 0))
path3.closePath()
我花了 2 个小时试图弄清楚它无济于事。请您给我一些关于如何实现这一目标的说明。
初始状态如下:
结束状态如下:
非常感谢您的帮助!
【问题讨论】:
【参考方案1】:最简单的方法...作弊。
不要尝试为它确实不需要它的形状的路径设置动画。
你应该做的是这样的。
创建您的最终状态视图。矩形,在屏幕底部,上面有 UI 等等...
这个最终状态视图不会移动。它会一直在这里。
现在创建另一个视图并将其作为子视图插入到最终状态视图下方。在此您可以添加一个切掉角角的形状图层。
现在您需要做的就是将该角度视图的位置向下设置动画,直到它完全低于最终状态视图。
如果它们是相同的颜色,这将产生动画形状路径的效果。
要获得不同的速度,您可以将矩形图层旋转 45 度。然后在视图向下滑动时将其设置为 0 度?
事实上,您可以使用旋转和移动的单个形状图层来做到这一点。
【讨论】:
【参考方案2】:要制作这种动画,您通常会使用CADisplayLink
(有点像计时器,但与显示更新而不是某个任意时间间隔相关联)来重复更改与所需形状关联的path
。我认为如果您使用 CAShapeLayer
并更改此形状的 path
属性,这是最简单的。
要完成这项工作,您需要一个函数来表示给定时间点的路径(或者更简单,沿着动画持续时间的某个时刻percentageComplete
的路径)。由于您有一个规则的形状(点数始终相同),您可以简单地在 startPoints
和 endPoints
的一些数组之间进行插值。
所以,创建形状层,捕获开始时间,启动显示链接,然后对于显示链接的每个“滴答”,计算animationDuration
已经过去的百分比,并更新形状层的路径相应地:
class ViewController: UIViewController
let animationDuration = 2.0
var displayLink: CADisplayLink!
var startTime: CFAbsoluteTime!
var shapeLayer: CAShapeLayer!
let goldRatio: CGFloat = 1.6180339887
var startPoints:[CGPoint]!
var endPoints:[CGPoint]!
override func viewDidLoad()
super.viewDidLoad()
self.startAnimation()
func startAnimation()
startPoints = [
CGPoint(x: 0, y: view.bounds.size.height),
CGPoint(x: 0, y: view.bounds.size.height - view.bounds.size.height / goldRatio),
CGPoint(x: view.bounds.size.width, y: 0),
CGPoint(x: view.bounds.size.width, y: view.bounds.size.height)
]
endPoints = [
CGPoint(x: 0, y: view.bounds.size.height),
CGPoint(x: 0, y: view.bounds.size.height * 0.75),
CGPoint(x: view.bounds.size.width, y: view.bounds.size.height * 0.75),
CGPoint(x: view.bounds.size.width, y: view.bounds.size.height)
]
assert(startPoints.count == endPoints.count, "Point counts don't match")
createShape()
startDisplayLink()
func startDisplayLink()
displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
startTime = CFAbsoluteTimeGetCurrent()
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
func stopDisplayLink()
displayLink.invalidate()
displayLink = nil
func handleDisplayLink(displayLink: CADisplayLink)
var percent = CGFloat((CFAbsoluteTimeGetCurrent() - startTime) / animationDuration)
if percent >= 1.0
percent = 1.0
stopDisplayLink()
updatePathBasedUponPercentComplete(percent)
func createShape()
shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.blueColor().CGColor
shapeLayer.strokeColor = UIColor.clearColor().CGColor
updatePathBasedUponPercentComplete(0.0)
view.layer.addSublayer(shapeLayer)
func updatePathBasedUponPercentComplete(percentComplete: CGFloat)
shapeLayer.path = pathBasedUponPercentComplete(percentComplete, frame: view.frame).CGPath
func pathBasedUponPercentComplete(percentComplete: CGFloat, frame: CGRect) -> UIBezierPath
var path = UIBezierPath()
for i in 0 ..< startPoints.count
let point = CGPoint(
x: startPoints[i].x + (endPoints[i].x - startPoints[i].x) * percentComplete,
y: startPoints[i].y + (endPoints[i].y - startPoints[i].y) * percentComplete
)
if i == 0
path.moveToPoint(point)
else
path.addLineToPoint(point)
path.closePath()
return path
【讨论】:
以上是关于如何使 UIBezierPath 中的两条线以不同的速度进行动画处理的主要内容,如果未能解决你的问题,请参考以下文章
查找表示 GPS 路线的两条线之间的距离(MATLAB、Java、C++ 或 Python)