这篇文章汇总了几种我在学习与工作中遇到的几种动画,在此记录一下以便自己以后复习,同时也希望与大家交流学习。
Talk is cheap, Show me the code.
1、画线动画及沿路径移动
self.view.backgroundColor = UIColor.init(red: 44/255.0, green: 34/255.0, blue: 85/255.0, alpha: 1)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 50, y: 100))
bezierPath.addLine(to: CGPoint(x: 325, y: 100))
bezierPath.addLine(to: CGPoint(x: 80, y: 250))
bezierPath.addLine(to: CGPoint(x: 275, y: 250))
bezierPath.addLine(to: CGPoint(x: self.view.frame.width/2.0, y: 220))
bezierPath.addLine(to: CGPoint(x: self.view.frame.width/2.0, y: 400))
bezierPath.addLine(to: CGPoint(x: 30, y: 400))
bezierPath.addLine(to: CGPoint(x: 345, y: 400))
//画线
let shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.purple.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 2
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineCap = kCALineCapRound
shapeLayer.path = bezierPath.cgPath
view.layer.addSublayer(shapeLayer)
let pathAnim = CABasicAnimation(keyPath: "strokeEnd")
pathAnim.duration = 5.0
pathAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
pathAnim.fromValue = 0//开始
pathAnim.toValue = 1//到100%
pathAnim.autoreverses = true// 动画按原路径返回
pathAnim.fillMode = kCAFillModeForwards
// pathAnim.isRemovedOnCompletion = false
pathAnim.repeatCount = Float.infinity
shapeLayer.add(pathAnim, forKey: "strokeEndAnim")
//视图沿路径移动
let moveV = UIImageView.init(image: UIImage.init(named: ""))
moveV.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
moveV.backgroundColor = UIColor.red
moveV.center = CGPoint(x: 50, y: 100)
view.addSubview(moveV)
let keyAnima = CAKeyframeAnimation.init(keyPath: "position")
// keyAnima.delegate = self
keyAnima.duration = 5.0
keyAnima.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
keyAnima.path = bezierPath.cgPath
keyAnima.fillMode = kCAFillModeForwards//动画开始之后layer的状态将保持在动画的最后一帧,而removedOnCompletion的默认属性值是 YES,所以为了使动画结束之后layer保持结束状态,应将removedOnCompletion设置为NO
keyAnima.isRemovedOnCompletion = false
moveV.layer.add(keyAnima, forKey: "moveAnimation")
2、水面波浪
lazy var waveDisplaylink = CADisplayLink()
lazy var firstWaveLayer = CAShapeLayer()
lazy var secondWaveLayer = CAShapeLayer()
var firstWaveColor: UIColor?
/// 水纹振幅
var waveA: CGFloat = 10
/// 水纹周期
var waveW: CGFloat = 1/30.0;
/// 位移
var offsetX: CGFloat = 0
/// 当前波浪高度Y
var currentK: CGFloat = 0
/// 水纹速度
var waveSpeed: CGFloat = 0
/// 水纹路宽度
var waterWaveWidth: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.init(red: 44/255.0, green: 34/255.0, blue: 85/255.0, alpha: 1)
self.view.layer.masksToBounds = true
setUpUI()
}
func setUpUI () {
// 波浪宽
waterWaveWidth = self.view.bounds.size.width
// 波浪颜色
firstWaveColor = UIColor.green
// 波浪速度
waveSpeed = 0.4 / CGFloat(M_PI)
// 设置闭环的颜色
firstWaveLayer.fillColor = UIColor.init(colorLiteralRed: 73/255.0, green: 142/255.0, blue: 178/255.0, alpha: 0.5).cgColor
// 设置边缘线的颜色
// firstWaveLayer.strokeColor = UIColor.blue.cgColor
firstWaveLayer.strokeStart = 0.0
firstWaveLayer.strokeEnd = 0.8
// 设置闭环的颜色
secondWaveLayer.fillColor = UIColor.init(colorLiteralRed: 73/255.0, green: 142/255.0, blue: 178/255.0, alpha: 0.5).cgColor
// 设置边缘线的颜色
// secondWaveLayer.strokeColor = UIColor.blue.cgColor
secondWaveLayer.strokeStart = 0.0
secondWaveLayer.strokeEnd = 0.8
self.view.layer.addSublayer(firstWaveLayer)
self.view.layer.addSublayer(secondWaveLayer)
// 设置波浪流动速度
waveSpeed = 0.05
// 设置振幅
waveA = 8
// 设置周期
waveW = 2 * CGFloat(M_PI) / self.view.bounds.size.width
// 设置波浪纵向位置
currentK = self.view.bounds.size.height / 2 //屏幕居中
waveDisplaylink = CADisplayLink(target: self, selector: #selector(getCurrentWave))
waveDisplaylink.add(to: RunLoop.current, forMode: .commonModes)
}
@objc private func getCurrentWave(disPlayLink: CADisplayLink) {
// 实时位移
offsetX += waveSpeed
setCurrentFirstWaveLayerPath()
}
private func setCurrentFirstWaveLayerPath() {
// 创建一个路径
let path = CGMutablePath()
var y = currentK
path.move(to: CGPoint(x: 0, y: y))
for i in 0...Int(waterWaveWidth) {
y = waveA * sin(waveW * CGFloat(i) + offsetX) + currentK
path.addLine(to: CGPoint(x: CGFloat(i), y: y))
}
path.addLine(to: CGPoint(x: waterWaveWidth, y: self.view.bounds.size.height))
path.addLine(to: CGPoint(x: 0, y: self.view.bounds.size.height))
path.closeSubpath()
firstWaveLayer.path = path
// 创建一个路径
let path2 = CGMutablePath()
var y2 = currentK
path2.move(to: CGPoint(x: 0, y: y))
for i in 0...Int(waterWaveWidth) {
y2 = waveA * sin(waveW * CGFloat(i) + offsetX - waterWaveWidth/2 ) + currentK
path2.addLine(to: CGPoint(x: CGFloat(i), y: y2))
}
path2.addLine(to: CGPoint(x: waterWaveWidth, y: self.view.bounds.size.height))
path2.addLine(to: CGPoint(x: 0, y: self.view.bounds.size.height))
path2.closeSubpath()
secondWaveLayer.path = path2
}
3、雪花飘落动画
func snowAnimation() {
//粒子发射器
let snowEmitter = CAEmitterLayer()
//粒子发射的位置
snowEmitter.emitterPosition = CGPoint(x: 100, y: 30)
//发射源的大小
snowEmitter.emitterSize = CGSize(width: self.view.bounds.size.width, height: 0)
//发射模式
snowEmitter.emitterMode = kCAEmitterLayerOutline
//发射源的形状
snowEmitter.emitterShape = kCAEmitterLayerLine
//创建雪花粒子
let cell = CAEmitterCell()
//粒子的名称
cell.name = "snow"
//粒子参数的速度乘数因子。越大出现的越快
cell.birthRate = 1.0
//存活时间
cell.lifetime = 120.0
//粒子速度
cell.velocity = -10
//粒子速度范围
cell.velocityRange = 10
//粒子y方向的加速度分量
cell.yAcceleration = 3
//周围发射角度
cell.emissionRange = CGFloat(0.5 * M_PI)
//粒子旋转角度范围
cell.spinRange = CGFloat(0.25 * Double.pi)
//粒子图片
cell.contents = UIImage.init(named: "snow")?.cgImage
//粒子颜色
cell.color = UIColor.white.cgColor
//设置阴影
snowEmitter.shadowOpacity = 1.0
snowEmitter.shadowRadius = 0.0
snowEmitter.shadowOffset = CGSize(width: 0.0, height: 1.0)
snowEmitter.shadowColor = UIColor.white.cgColor
// 将粒子添加到粒子发射器上
snowEmitter.emitterCells = [cell]
self.view.layer .addSublayer(snowEmitter)
}
4、烟花动画
/// 烟花粒子动画
func fireworksAnimation() {
//分为3种粒子,子弹粒子,爆炸粒子,散开粒子
let fireworkEmitter = CAEmitterLayer()
fireworkEmitter.emitterPosition = CGPoint(x: self.view.bounds.size.width/2.0, y: self.view.bounds.size.height)
fireworkEmitter.emitterSize = CGSize(width: self.view.bounds.size.width/2.0, height: 0.0)
fireworkEmitter.emitterMode = kCAEmitterLayerOutline
fireworkEmitter.emitterShape = kCAEmitterLayerLine
fireworkEmitter.renderMode = kCAEmitterLayerAdditive
fireworkEmitter.seed = (arc4random()%100)+1
// Create the rocket
let rocket = CAEmitterCell()
rocket.birthRate = 1.0
rocket.velocity = 380
rocket.velocityRange = 100
rocket.yAcceleration = 75
rocket.lifetime = 1.02
//小圆球图片
rocket.contents = UIImage.init(named: "dot")?.cgImage
rocket.scale = 0.2;
rocket.color = UIColor.yellow.cgColor
rocket.greenRange = 1.0; // different colors
rocket.redRange = 1.0;
rocket.blueRange = 1.0;
rocket.spinRange = CGFloat(M_PI); // slow spin
// the burst object cannot be seen, but will spawn the sparks
// we change the color here, since the sparks inherit its value
let burst = CAEmitterCell()
burst.birthRate = 1.0; // at the end of travel
burst.velocity = 0; //速度为0
burst.scale = 2.5; //大小
burst.redSpeed = -1.5// shifting
burst.blueSpeed = +1.5
burst.greenSpeed = +1.0
burst.lifetime = 0.35; //存在时间
// and finally, the sparks
let spark = CAEmitterCell()
spark.birthRate = 400;
spark.velocity = 125;
spark.emissionRange = CGFloat(2 * M_PI); // 360 度
spark.yAcceleration = 75; // gravity
spark.lifetime = 3;
//星星图片
spark.contents = UIImage.init(named: "star")?.cgImage
spark.scaleSpeed = -0.2;
spark.greenSpeed = -0.1;
spark.redSpeed = 0.4;
spark.blueSpeed = -0.1;
spark.alphaSpeed = -0.25;
spark.spin = CGFloat(2 * M_PI)
spark.spinRange = CGFloat(2 * M_PI)
// 3种粒子组合,可以根据顺序,依次烟花弹-烟花弹粒子爆炸-爆炸散开粒子
fireworkEmitter.emitterCells = [rocket];
rocket.emitterCells = [burst];
burst.emitterCells = [spark];
self.view.layer.addSublayer(fireworkEmitter)
}
5、火苗效果
/// 火焰效果
func fireAnimation() {
// 发射器在xy平面的中心位置
fireEmitter.emitterPosition = view.center
// 发射器的尺寸大小
// fireEmitter.emitterSize = CGSize(width: 20, height: 60)
// 发射器的发射模式
// fireEmitter.emitterMode = kCAEmitterLayerOutline
// // 发射器的形状
fireEmitter.emitterShape = kCAEmitterLayerCircle
// 发射器渲染模式
fireEmitter.renderMode = kCAEmitterLayerAdditive
// 发射单元 - 火焰
let fire = CAEmitterCell()
// 粒子的创建速率,默认为1/s。
fire.birthRate = 200
// 粒子存活时间
fire.lifetime = 0.2
// 粒子的生存时间容差
fire.lifetimeRange = 0.5
fire.color = UIColor.init(colorLiteralRed: 0.8, green: 0.4, blue: 0.2, alpha: 0.1) .cgColor
fire.contents = UIImage(named: "fire.png")?.cgImage
fire.name = "fire"
// 粒子的速度
fire.velocity = 35
// 粒子动画的速度容差
fire.velocityRange = 10
// 粒子在xy平面的发射角度
fire.emissionLongitude = CGFloat(M_PI + M_PI_2)
// 粒子发射角度的容差
fire.emissionRange = CGFloat(M_PI_2)
// 缩放速度
fire.scaleSpeed = 0.3
// 旋转度
// fire.spin = 0.2
fireEmitter.emitterCells = [fire]
view.layer.addSublayer(fireEmitter)
}
6、流星动画
var timer : Timer!
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
timer.invalidate()
timer = nil
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.init(red: 44/255.0, green: 34/255.0, blue: 85/255.0, alpha: 1)
// Do any additional setup after loading the view.
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(liuXingAnimation), userInfo: nil, repeats: true)
}
///流星动画
func liuXingAnimation() {
let rect = CGRect(x: self.view.bounds.size.width, y: self.view.bounds.height, width: 100, height: 100)
let emitter = CAEmitterLayer()
emitter.frame = rect
self.view.layer.addSublayer(emitter)
emitter.renderMode = kCAEmitterLayerAdditive//合并粒子重叠部分的亮度使得看上去更亮
emitter.emitterPosition = CGPoint(x: rect.width/2, y:rect.height/2)
let cell = CAEmitterCell()
let image = UIImage(named: "liuxing")!
cell.contents = image.cgImage
cell.birthRate = 500 //每秒产生150个粒子
cell.lifetime = 5.0
cell.color = UIColor.init(red: 1, green: 1, blue: 1, alpha: 0.5).cgColor
cell.alphaSpeed = -0.6//粒子的透明度每过一秒就减少0.4
cell.velocity = 50
cell.velocityRange = 20 //初始速度值变化的范围 30 ~ 70
cell.emissionLongitude = CGFloat(-Double.pi/2/180*45)//向上(x-y平面的发射方向)
cell.emissionRange = CGFloat(Double.pi/2/180)//围绕发射方向的弧度数
cell.scale = 0.2
emitter.emitterCells = [cell]
self.view.layer.addSublayer(emitter)
let aPath = UIBezierPath()
let height : UInt32 = UInt32(self.view.bounds.height-50)
let randomY = CGFloat(arc4random()%height)
aPath.move(to: CGPoint(x: self.view.bounds.width, y: randomY))
aPath.addLine(to: CGPoint(x: -100, y: self.view.bounds.width/2+randomY))
let animation = CAKeyframeAnimation(keyPath: "position")
animation.duration = 5;
animation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseIn)
animation.repeatCount = 1
animation.path = aPath.cgPath
// animation.calculationMode = kCAAnimationPaced
animation.setValue("liuxing", forKey: "liuxing")
// animation.rotationMode = kCAAnimationRotateAutocell
emitter.add(animation, forKey: "moveTheSquare")
// animation.delegate = self
}
7、Lottie加载动画
使用这个动画需要在项目中加入Lottie三方库,我在项目中使用了cocoapod。只需要在其中加入 pod 'lottie-ios' 即可。 其中的动画使用AE制作生成的 json 文件。 你也可以去这里下载你喜欢的动效。
animationArr = ["LottieLogo1_masked", "9squares-AlBoardman", "HamburgerArrow", "IconTransitions", "LottieLogo1", "LottieLogo2", "MotionCorpse-Jrcanest", "PinJump"];
let temp = Int(arc4random()%(UInt32(animationArr.count)))
animationStr = animationArr[temp]
contentView.layer.masksToBounds = true
animationView = LOTAnimationView(name: animationStr)
animationView.frame = self.contentView.bounds
animationView.contentMode = .scaleAspectFill
self.contentView.addSubview(animationView)
animationView.play{ (finished) in
// Do Something
}
8、后续待更新...
9、后记
如果大家对这些效果有兴趣,可以去github下载源码,若对您有帮助的话,请多多star哦!