CAKeyframeAnimation
CAKeyframeAnimation
-
认识CAKeyframeAnimation
CAKeyframeAnimation是可以在layer层按照一组关键帧执行的动画。
-
属性介绍
keyPath : 动画的关键路径属性(做什么动画)
path : 基于点的属性的路径,对于包含CGPoint数据类型的层属性,您分配给该属性的路径对象定义了该属性在动画长度上的值。如果指定此属性的值,则忽略值属性中的任何数据
values:关键帧值表示动画必须执行的值,此属性中的值仅在path属性的值为nil时才使用。根据属性的类型,您可能需要用NSValue对象的NSNumber包装这个数组中的值。对于一些核心图形数据类型,您可能还需要将它们转换为id,然后再将它们添加到数组中。将给定的关键帧值应用于该层的时间取决于动画时间,由calculationMode、keyTimes和timingFunctions属性控制。关键帧之间的值是使用插值创建的,除非将计算模式设置为kcaanimation离散
duration : 动画时长
repeatCount : 重复次数
calculationMode : 动画计算方式
kCAAnimationLinear : 默认差值
kCAAnimationDiscrete : 逐帧显示
kCAAnimationPaced : 匀速 无视keyTimes
kCAAnimationCubic : keyValue之间曲线平滑 可用 tensionValues,continuityValues,biasValues 调整
kCAAnimationCubicPaced : keyValue之间平滑差值 无视keyTimes
KeyTimes : 一个可选的NSNumber对象数组,它定义了给定关键帧片段的时间
timingFunctions:一个可选的CAMediaTimingFunction对象数组,它定义每个关键帧段的节奏
kCAMediaTimingFunctionLinear:线性起搏,使动画在其持续时间内均匀地发生
kCAMediaTimingFunctionEaseIn:使一个动画开始缓慢,然后加速,随着它的进程
kCAMediaTimingFunctionEaseOut: 使动画快速开始,然后缓慢地进行
kCAMediaTimingFunctionEaseInEaseOut:使动画开始缓慢,在其持续时间的中间加速,然后在完成之前再放慢速度
kCAMediaTimingFunctionDefault: 默认,确保动画的时间与大多数系统动画的匹配
rotationMode : 旋转方式
kCAAnimationRotateAuto : 自动
kCAAnimationRotateAutoReverse : 自动翻转 不设置则不旋转
-
示例
import UIKit
import CoreGraphics
import QuartzCore
//使用关键帧动画
class Style1Button: UIButton {
var top: CAShapeLayer! = CAShapeLayer()
var bottom: CAShapeLayer! = CAShapeLayer()
var middle: CAShapeLayer! = CAShapeLayer()
//keyframe
let shortStrokeTop1 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 16))
line.addLine(to: CGPoint(x: 34, y: 16))
}
let shortStrokeTop2 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 17.5))
line.addLine(to: CGPoint(x: 30, y: 16))
}
let shortStrokeTop3 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 19))
line.addLine(to: CGPoint(x: 26, y: 17))
}
let shortStrokeTop4 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 20.5))
line.addLine(to: CGPoint(x: 22, y: 18))
}
let shortStrokeTop5 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 22))
line.addLine(to: CGPoint(x: 18, y: 19))
}
let shortStrokeBottom1 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 28))
line.addLine(to: CGPoint(x: 34, y: 28))
}
let shortStrokeBottom2 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 26.5))
line.addLine(to: CGPoint(x: 30, y: 28))
}
let shortStrokeBottom3 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 25))
line.addLine(to: CGPoint(x: 26, y: 27))
}
let shortStrokeBottom4 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 23.5))
line.addLine(to: CGPoint(x: 22, y: 26))
}
let shortStrokeBottom5 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 22))
line.addLine(to: CGPoint(x: 18, y: 25))
}
let shortStrokeMiddle1 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 22))
line.addLine(to: CGPoint(x: 34, y: 22))
}
let shortStrokeMiddle2 = UIBezierPath().then { (line) in
line.move(to: CGPoint(x: 10, y: 22))
line.addLine(to: CGPoint(x: 30, y: 22))
}
override init(frame: CGRect) {
super.init(frame: frame)
self.top.path = shortStrokeTop1.cgPath
self.middle.path = shortStrokeMiddle1.cgPath
self.bottom.path = shortStrokeBottom1.cgPath
for layer in [self.top,self.middle,self.bottom] {
layer?.fillColor = nil
layer?.strokeColor = UIColor.white.cgColor
layer?.lineWidth = 2
layer?.miterLimit = 4
layer?.lineCap = kCALineCapRound
layer?.bounds = (layer?.path?.boundingBox)!
self.layer.addSublayer(layer!)
}
self.top.position = CGPoint(x: 22, y: 16)
self.middle.position = CGPoint(x: 22, y: 22)
self.bottom.position = CGPoint(x: 22, y: 28)
}
var showsMenu: Bool = false {
didSet {
if showsMenu {
let topValues = [shortStrokeTop1.cgPath,shortStrokeTop2.cgPath,shortStrokeTop3.cgPath,shortStrokeTop4.cgPath,shortStrokeTop5.cgPath]
self.top.add(pathAnimation(topValues), forKey: "TopPath")
let bottomValues = [shortStrokeBottom1.cgPath,shortStrokeBottom2.cgPath,shortStrokeBottom3.cgPath,shortStrokeBottom4.cgPath,shortStrokeBottom5.cgPath]
self.bottom.add(pathAnimation(bottomValues), forKey: "BottomPath")
let middleValues = [shortStrokeMiddle1.cgPath,shortStrokeMiddle2.cgPath]
self.middle.add(pathAnimation(middleValues), forKey: "MiddlePath")
}else {
let topValues = Array([shortStrokeTop1.cgPath,shortStrokeTop2.cgPath,shortStrokeTop3.cgPath,shortStrokeTop4.cgPath,shortStrokeTop5.cgPath].reversed())
self.top.add(pathAnimation(topValues), forKey: "TopPathReversed")
let bottomValues = Array([shortStrokeBottom1.cgPath,shortStrokeBottom2.cgPath,shortStrokeBottom3.cgPath,shortStrokeBottom4.cgPath,shortStrokeBottom5.cgPath].reversed())
self.bottom.add(pathAnimation(bottomValues), forKey: "BottomPathReversed")
let middleValues = Array([shortStrokeMiddle1.cgPath,shortStrokeMiddle2.cgPath].reversed())
self.middle.add(pathAnimation(middleValues), forKey: "MiddlePathReversed")
}
}
}
func pathAnimation(_ values: [CGPath]) ->CAKeyframeAnimation {
let pathAnimation = CAKeyframeAnimation(keyPath: "path")
pathAnimation.values = values
pathAnimation.duration = 0.5
pathAnimation.isRemovedOnCompletion = false
pathAnimation.fillMode = kCAFillModeForwards
pathAnimation.beginTime = 0
pathAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)]
pathAnimation.repeatCount = 1
return pathAnimation
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}