0
点赞
收藏
分享

微信扫一扫

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

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

项目背景

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

搜了网上很多资料,大多数都是AE的实现的动画,那在​​App​​上能不能实现这个效果呢?

说干就干。

项目搭建

首先,创建一个新的​​SwiftUI​​​项目,命名为​​ParticleEffects​​。

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

粒子运动

为了实现粒子动画效果,我们这里使用到的是​​GeometryEffect​​​几何效果函数,它可以实现​​Animatable​​​和​​ViewModifier​​这两个协议,来模拟关键帧动画。

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

上述代码是​​GeometryEffect​​​在​​SwiftUI​​​中的应用,当我们创建一个结构体​​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

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

因此我们声明了一个​​Double​​​类型的参数​​time​​来作为时间,声明一个​​Double​​​类型的常量​​v0​​作为粒子的随机初始速度,声明了一个​​Double​​​类型的常量​​alpha​​作为粒子的随机投射角度,声明了一个常量​​g​​来作为重力,最后通过​​get​​​和​​set​​​说明计算属性​​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

上述代码中,我们使用​​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搭建一个爱心粒子动画~_Swift_02

上述代码中,我们构建了3个​​Image​​​图片,使用​​ZStack​​​层叠视图包裹在一起,每一个​​Image​​​图片都使用​​modifier​​​修饰符使用​​ParticleEffectView​​​粒子效果,最后整个​​背景颜色​​填充为黑色。

项目预览

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

恭喜你,完成了整个项目的全部内容!

快来动手试试吧。

如果本专栏对你有帮助,不妨点赞、评论、关注~

举报

相关推荐

用html写一个爱心

0 条评论