Swift - 沿路径运动的动画实现(圆形、曲线、直线等路径轨迹)
(本文代码已升级至Swfit4)
通常要实现沿着复杂路径的动画,我们可以借助关键帧动画(CAKeyframeAnimation)来实现。由于 CAKeyframeAnimation 提供了的便利的 path 属性,我们只需要对其设置相应的路径即可。
1,准备工作
首先我们在页面上添加一个橙色的方块,后面通过对这个方块添加轨迹动画来演示CAKeyframeAnimation的使用。

import UIKit class ViewController: UIViewController { var square:UIView! override func viewDidLoad() { super.viewDidLoad() square = UIView(frame:CGRect(x:0, y:0, width:20, height:20)) square.backgroundColor = UIColor.orange self.view.addSubview(square) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
2,做圆周运动
下面样例中方块会沿着圆形的轨迹做圆周运动(只播放一次)
(2)将 calculationMode 设置为 kCAAnimationPaced,让 Core Animation 向被驱动的对象施加一个恒定速度,不管路径的各个线段有多长。

let centerX = view.bounds.size.width/2 let boundingRect = CGRect(x:centerX-75, y:50, width:150, height:150) let orbit = CAKeyframeAnimation(keyPath:"position") orbit.duration = 3 orbit.path = CGPath(ellipseIn: boundingRect, transform: nil) orbit.calculationMode = kCAAnimationPaced square.layer.add(orbit, forKey:"Move")
默认情况下动画执行完毕后,图形会恢复到动画执行前的状态。比如上面样例,方块移动一周后又会跑到页面坐上角位置。如果想让方块保持显示动画执行后的状态,有如下两种方式:
(1)动画下方代码直接设置方块最终位置坐标
let centerX = view.bounds.size.width/2 let boundingRect:CGRect = CGRectMake(centerX-75, 50, 150, 150) let orbit = CAKeyframeAnimation(keyPath:"position") orbit.duration = 3 orbit.path = CGPathCreateWithEllipseInRect(boundingRect,nil) orbit.calculationMode = kCAAnimationPaced square.layer.addAnimation(orbit,forKey:"Move") //下面代码让方块运动一周后停留在终点位置(否则会回到原来的位置) square.layer.position = CGPoint(x:centerX+75, y:125)
let centerX = view.bounds.size.width/2 let boundingRect = CGRect(x:centerX-75, y:50, width:150, height:150) let orbit = CAKeyframeAnimation(keyPath:"position") orbit.duration = 3 orbit.path = CGPath(ellipseIn: boundingRect, transform: nil) orbit.calculationMode = kCAAnimationPaced orbit.isRemovedOnCompletion = false orbit.fillMode = kCAFillModeForwards square.layer.add(orbit,forKey:"Move")
repeatCount 参数可以设置动画的重复次数,将其设为 HUGE 可以让方块圆周运动动画一直循环播放下去。
let centerX = view.bounds.size.width/2 let boundingRect = CGRect(x:centerX-75, y:50, width:150, height:150) let orbit = CAKeyframeAnimation(keyPath:"position") orbit.duration = 3 orbit.path = CGPath(ellipseIn: boundingRect, transform: nil) orbit.calculationMode = kCAAnimationPaced orbit.repeatCount = HUGE square.layer.add(orbit, forKey:"Move")
5,让方块沿着路径旋转
设置 rotationMode 属性为 kCAAnimationRotateAuto 可以让方块沿着路径旋转,即方块不管移动到哪个位置,它始终朝向圆心。

let centerX = view.bounds.size.width/2 let boundingRect = CGRect(x:centerX-75, y:50, width:150, height:150) let orbit = CAKeyframeAnimation(keyPath:"position") orbit.duration = 3 orbit.path = CGPath(ellipseIn: boundingRect, transform: nil) orbit.calculationMode = kCAAnimationPaced orbit.repeatCount = HUGE orbit.rotationMode = kCAAnimationRotateAuto square.layer.add(orbit,forKey:"Move")
通过 CGPathCreateMutable,我们可以将多条弧线、直线等拼接成一个复杂的路径,让方块在这个路径上运动。

let centerX = view.bounds.size.width/2 //创建用于转移坐标的Transform,这样我们不用按照实际显示做坐标计算 let transform = CGAffineTransform(translationX: centerX, y: 50) let path = CGMutablePath() path.move(to: CGPoint(x:0 ,y:0), transform: transform) path.addLine(to: CGPoint(x:0 ,y:75), transform: transform) path.addLine(to: CGPoint(x:75 ,y:75), transform: transform) path.addArc(center: CGPoint(x:0 ,y:75), radius: 75, startAngle: 0, endAngle: CGFloat(1.5 * .pi), clockwise: false, transform: transform) let orbit = CAKeyframeAnimation(keyPath:"position") orbit.duration = 3 orbit.path = path orbit.calculationMode = kCAAnimationPaced orbit.repeatCount = HUGE //orbit.rotationMode = kCAAnimationRotateAuto square.layer.add(orbit,forKey:"Move")
7,使用动画组实现多种动画的组合
通过动画组(CAAnimationGroup),我们可以将多个动画组合在一起同时播放。
下面的样例中,小方块沿着轨迹运动的同时,自身还会快速地旋转。(方块移动一周,自身会旋转100次)

//旋转动画 let rotateAnimation = CAKeyframeAnimation(keyPath:"transform.rotation.z") rotateAnimation.values = [0,200 * Float.pi] //轨迹动画路径 let centerX = view.bounds.size.width/2 //创建用于转移坐标的Transform,这样我们不用按照实际显示做坐标计算 let transform:CGAffineTransform = CGAffineTransform(translationX: centerX, y: 50) let path = CGMutablePath() path.move(to: CGPoint(x:0 ,y:0), transform: transform) path.addLine(to: CGPoint(x:0 ,y:75), transform: transform) path.addLine(to: CGPoint(x:75 ,y:75), transform: transform) path.addArc(center: CGPoint(x:0 ,y:75), radius: 75, startAngle: 0, endAngle: CGFloat(1.5 * .pi), clockwise: false, transform: transform) //轨迹动画 let orbitAnimation = CAKeyframeAnimation(keyPath:"position") orbitAnimation.path = path orbitAnimation.calculationMode = kCAAnimationPaced //组合两个动画 let animationgroup = CAAnimationGroup() animationgroup.animations = [rotateAnimation, orbitAnimation] animationgroup.duration = 4 animationgroup.repeatCount = HUGE square.layer.add(animationgroup,forKey:"Move")
如何能让运动轨迹绘制出来呢
很好,mark下