携手创作,一起生长!这是我参与「日新计划 8 月更文挑战」的第14天,点击检查活动详情。

在本章中,你将学会用SwiftUI搭建一个粒子特效动画

项目背景

近期某音上的粒子特效很火,一个中心点不断涌出各种色彩的粒子,构成一种炫彩缤纷的作用。

搜了网上许多材料,大多数都是AE的完结的动画,那在App上能不能完结这个作用呢?

说干就干。

项目搭建

首要,创立一个新的SwiftUI项目,命名为ParticleEffects

不断涌出的爱意,使用SwiftUI搭建一个爱心粒子动画~

粒子运动

为了完结粒子动画作用,咱们这儿运用到的是GeometryEffect几何作用函数,它可以完结AnimatableViewModifier这两个协议,来模仿关键动画。

// 粒子动画
struct ParticleMotion: GeometryEffect {
func effectValue(size: CGSize) -> ProjectionTransform {
<#code#>
}
}

上述代码是GeometryEffectSwiftUI中的运用,当咱们创立一个结构体ParticleMotion遵从GeometryEffect协议时,SwiftUI会自动协助咱们补全该协议所包括的必要代码。

咱们可以看到在ParticleMotion结构体中,SwiftUI弥补了一个方法effectValue,它传入一个CGSize类型的size参数,返回ProjectionTransform投影改换作用。

在计算机图形学中,投影改换是把3D几何体转换成一种可作为二维图画渲染的方法。

然后咱们声明几个必要变量来完结粒子运动动画,示例:

var time: Double
let v0: Double= Double.random(in: 40...80)
let alpha: Double = Double.random(in: 0.0 ..< 2 * Double.pi)
let g = 15 * 9.81
var animatableData: Double {
get { time }
set { time = newValue }
 }

粒子作用咱们可以当作一种某一个视图跟着重力场不断做布朗运动,简略来说就是做随机运动

因而咱们声明晰一个Double类型的参数time来作为时刻,声明一个Double类型的常量v0作为粒子的随机初始速度,声明晰一个Double类型的常量alpha作为粒子的随机投射视点,声明晰一个常量g来作为重力,最终经过getset说明计算特点time是可读写的。

经过上面声明好的参数,咱们来完结投影改换运动,示例:

func effectValue(size: CGSize) -> ProjectionTransform {
let dx = v0 * time * cos(alpha)
let dy = v0 * sin(alpha) * time - 0.5 * g * time * time
let affineTransform = CGAffineTransform(translationX: CGFloat(dx), y: CGFloat(-dy))
return ProjectionTransform(affineTransform)
}

上述代码中运用到的数学公式这儿简略说明下,咱们运用投影改换中的affineTransform二维平面改换,这儿的translationX参数代表X轴平移系数为【随机的初始速度*时刻*随机投射视点余弦角】,Y轴平移系数为【随机初始速度*随机投射视点正弦角*时刻-0.5倍重力*时刻平方】。

粒子运动主要运用的原理是随机的接连位置形变,来模仿重力场下的布朗运动。

粒子作用

完结粒子运动方法后,咱们来完结粒子视图,首要咱们需求运用到ViewModifier特点修饰符来完结样式粒子作用,然后将ViewModifier特点修饰符赋予视图,示例:

//粒子视图
struct ParticleEffectView: ViewModifier {
func body(content: Content) -> some View {
<#code#>
}
}

上述代码中,咱们声明晰一个结构体ParticleEffectView遵从ViewModifier协议。

然后创立了一个标准视图用来构建视图样式作用,咱们还需求声明几个变量作为前期预备,示例:

let count: Int
let duration: Double = 2.0
@State var time: Double = 0.0

上述代码中,咱们声明晰一个Int类型常量count代表粒子数量,声明晰一个Double类型的常量duration来代表粒子持续时刻,声明晰一个Double类型的变量time代表时刻。

然后咱们在粒子视图中构建样式,示例:

func body(content: Content) -> some View {
let animation = Animation.linear(duration: duration).repeatForever(autoreverses: false)
ZStack {
ForEach(0 ..< count, id: \.self) { index in
content
.scaleEffect(CGFloat((duration - self.time) / duration))
.modifier(ParticleMotion(time: self.time))
.opacity((duration - self.time) / duration)
.animation(animation.delay(Double.random(in: 0 ..< self.duration)))
.blendMode(.plusLighter)
}
.onAppear {
withAnimation {
self.time = duration
}
}
}
}

上述代码中,咱们运用ForEach循环依据粒子数量count循环创立粒子,然后设置了粒子的scaleEffect缩放作用,设置粒子的modifier修饰为之前构建好的ParticleMotion粒子运动,运用opacity修饰符设置粒子的透明度,运用animation修饰符设置了粒子的延迟动画,运用blendMode修饰符设置了粒子的进行叠加图画计算。

粒子视图

完结上述预备后,咱们来正式构建视图部分内容。示例:

struct ContentView: View {
var body: some View {
ZStack {
Color.black
Image(systemName: "heart.fill")
.foregroundColor(Color.red)
.font(.system(size: 60))
.modifier(ParticleEffectView(count: 100))
Image(systemName: "heart.fill")
.foregroundColor(Color.green)
.font(.system(size: 60))
.modifier(ParticleEffectView(count: 100))
Image(systemName: "heart.fill")
.foregroundColor(Color.blue)
.font(.system(size: 60))
.modifier(ParticleEffectView(count: 100))
}
.edgesIgnoringSafeArea(.all)
}
}

不断涌出的爱意,使用SwiftUI搭建一个爱心粒子动画~

上述代码中,咱们构建了3个Image图片,运用ZStack层叠视图包裹在一起,每一个Image图片都运用modifier修饰符运用ParticleEffectView粒子作用,最终整个背景色彩填充为黑色。

项目预览

不断涌出的爱意,使用SwiftUI搭建一个爱心粒子动画~

祝贺你,完结了整个项目的全部内容!

快来着手试试吧。

如果本专栏对你有协助,不妨点赞、评论、重视~