這篇文章匯總了幾種我在學(xué)習(xí)與工作中遇到的幾種動畫,在此記錄一下以便自己以后復(fù)習(xí),同時也希望與大家交流學(xué)習(xí)。
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的狀態(tài)將保持在動畫的最后一幀,而removedOnCompletion的默認屬性值是 YES,所以為了使動畫結(jié)束之后layer保持結(jié)束狀態(tài),應(yīng)將removedOnCompletion設(shè)置為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)
// 設(shè)置閉環(huán)的顏色
firstWaveLayer.fillColor = UIColor.init(colorLiteralRed: 73/255.0, green: 142/255.0, blue: 178/255.0, alpha: 0.5).cgColor
// 設(shè)置邊緣線的顏色
// firstWaveLayer.strokeColor = UIColor.blue.cgColor
firstWaveLayer.strokeStart = 0.0
firstWaveLayer.strokeEnd = 0.8
// 設(shè)置閉環(huán)的顏色
secondWaveLayer.fillColor = UIColor.init(colorLiteralRed: 73/255.0, green: 142/255.0, blue: 178/255.0, alpha: 0.5).cgColor
// 設(shè)置邊緣線的顏色
// secondWaveLayer.strokeColor = UIColor.blue.cgColor
secondWaveLayer.strokeStart = 0.0
secondWaveLayer.strokeEnd = 0.8
self.view.layer.addSublayer(firstWaveLayer)
self.view.layer.addSublayer(secondWaveLayer)
// 設(shè)置波浪流動速度
waveSpeed = 0.05
// 設(shè)置振幅
waveA = 8
// 設(shè)置周期
waveW = 2 * CGFloat(M_PI) / self.view.bounds.size.width
// 設(shè)置波浪縱向位置
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() {
// 創(chuàng)建一個路徑
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
// 創(chuàng)建一個路徑
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() {
//粒子發(fā)射器
let snowEmitter = CAEmitterLayer()
//粒子發(fā)射的位置
snowEmitter.emitterPosition = CGPoint(x: 100, y: 30)
//發(fā)射源的大小
snowEmitter.emitterSize = CGSize(width: self.view.bounds.size.width, height: 0)
//發(fā)射模式
snowEmitter.emitterMode = kCAEmitterLayerOutline
//發(fā)射源的形狀
snowEmitter.emitterShape = kCAEmitterLayerLine
//創(chuàng)建雪花粒子
let cell = CAEmitterCell()
//粒子的名稱
cell.name = "snow"
//粒子參數(shù)的速度乘數(shù)因子。越大出現(xiàn)的越快
cell.birthRate = 1.0
//存活時間
cell.lifetime = 120.0
//粒子速度
cell.velocity = -10
//粒子速度范圍
cell.velocityRange = 10
//粒子y方向的加速度分量
cell.yAcceleration = 3
//周圍發(fā)射角度
cell.emissionRange = CGFloat(0.5 * M_PI)
//粒子旋轉(zhuǎn)角度范圍
cell.spinRange = CGFloat(0.25 * Double.pi)
//粒子圖片
cell.contents = UIImage.init(named: "snow")?.cgImage
//粒子顏色
cell.color = UIColor.white.cgColor
//設(shè)置陰影
snowEmitter.shadowOpacity = 1.0
snowEmitter.shadowRadius = 0.0
snowEmitter.shadowOffset = CGSize(width: 0.0, height: 1.0)
snowEmitter.shadowColor = UIColor.white.cgColor
// 將粒子添加到粒子發(fā)射器上
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種粒子組合,可以根據(jù)順序,依次煙花彈-煙花彈粒子爆炸-爆炸散開粒子
fireworkEmitter.emitterCells = [rocket];
rocket.emitterCells = [burst];
burst.emitterCells = [spark];
self.view.layer.addSublayer(fireworkEmitter)
}
5、火苗效果
/// 火焰效果
func fireAnimation() {
// 發(fā)射器在xy平面的中心位置
fireEmitter.emitterPosition = view.center
// 發(fā)射器的尺寸大小
// fireEmitter.emitterSize = CGSize(width: 20, height: 60)
// 發(fā)射器的發(fā)射模式
// fireEmitter.emitterMode = kCAEmitterLayerOutline
// // 發(fā)射器的形狀
fireEmitter.emitterShape = kCAEmitterLayerCircle
// 發(fā)射器渲染模式
fireEmitter.renderMode = kCAEmitterLayerAdditive
// 發(fā)射單元 - 火焰
let fire = CAEmitterCell()
// 粒子的創(chuàng)建速率,默認為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平面的發(fā)射角度
fire.emissionLongitude = CGFloat(M_PI + M_PI_2)
// 粒子發(fā)射角度的容差
fire.emissionRange = CGFloat(M_PI_2)
// 縮放速度
fire.scaleSpeed = 0.3
// 旋轉(zhuǎn)度
// 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 //每秒產(chǎn)生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平面的發(fā)射方向)
cell.emissionRange = CGFloat(Double.pi/2/180)//圍繞發(fā)射方向的弧度數(shù)
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、后續(xù)待更新...
9、后記
如果大家對這些效果有興趣,可以去github下載源碼,若對您有幫助的話,請多多star哦!