Vortex 是一个强大的、高性能的 SwiftUI 粒子系统库,让你只需几行代码就能创建漂亮的特效,比如火焰、雨、烟和雪。
Vortex 内置了一系列特效,比如烟花、魔法、彩屑等等,但你也可以创建完全自定义的特效,以满足你的需求。
此框架兼容 iOS、macOS、tvOS、watchOS 和 visionOS。
Vortex 使用 Swift Package Manager,所以你应该使用 Xcode 添加一个包依赖项,地址为 https://github.com/twostraws/Vortex。
完成之后,在你的 Swift 代码中需要的地方导入 Vortex
import Vortex
在这个仓库的 Assets 目录中,你会找到三个示例粒子图像供你使用,但你也可以使用各种 SwiftUI 视图和形状,而不仅仅是图像。
这个仓库包含一个跨平台示例项目,演示了所有预设的使用。该示例项目使用 SwiftUI 构建,需要 iOS 17、macOS 14 或 visionOS 1。
渲染 Vortex 粒子系统需要两个步骤
VortexSystem 的一个实例,并配置你希望粒子如何表现。 必须提供要渲染的粒子的标签名称列表。VortexView 添加到你的 SwiftUI 视图层级中,传入要渲染的粒子系统,以及用于粒子的所有视图,并使用步骤 1 中的相同名称进行标记。有很多内置的粒子系统设计,比如雨
VortexView(.rain) {
Circle()
.fill(.white)
.frame(width: 32)
.tag("circle")
}
烟花
VortexView(.fireworks) {
Circle()
.fill(.white)
.blendMode(.plusLighter)
.frame(width: 32)
.tag("circle")
}
和火焰
VortexView(.fire) {
Circle()
.fill(.white)
.blendMode(.plusLighter)
.blur(radius: 3)
.frame(width: 32)
.tag("circle")
}
注意
每个预设都旨在查找一个或多个标签;请查看下面的文档以获取正确的标签。
你也可以创建完全自定义的效果,比如这样
struct ContentView: View {
var body: some View {
VortexView(createSnow()) {
Circle()
.fill(.white)
.blur(radius: 5)
.frame(width: 32)
.tag("circle")
}
}
func createSnow() -> VortexSystem {
let system = VortexSystem(tags: ["circle"])
system.position = [0.5, 0]
system.speed = 0.5
system.speedVariation = 0.25
system.lifespan = 3
system.shape = .box(width: 1, height: 0)
system.angle = .degrees(180)
system.angleRange = .degrees(20)
system.size = 0.25
system.sizeVariation = 0.5
return system
}
}
注意
除非你专门使用 yourSystem.makeUniqueCopy() 请求复制,否则 VortexView 不会复制你提供的粒子系统。 这允许你创建一次粒子系统并在多个地方重复使用它,而不会丢失其状态。
虽然许多粒子系统不断发射粒子,但这不是必需的——你也可以根据需要创建爆发的粒子,例如,用户按下按钮时发射的彩屑炮。
这遵循与 SwiftUI 中类似的方法,例如 ScrollView 和 ScrollViewReader:将你的 VortexView 包装在 VortexViewReader 中,后者会传递给你一个 VortexProxy 对象,该对象能够操作它找到的第一个粒子系统。
例如,这使用了内置的 .confetti 效果,然后使用 Vortex 代理对象根据需要触发粒子爆发
VortexViewReader { proxy in
VortexView(.confetti) {
Rectangle()
.fill(.white)
.frame(width: 16, height: 16)
.tag("square")
Circle()
.fill(.white)
.frame(width: 16)
.tag("circle")
}
Button("Burst", action: proxy.burst)
}
你还可以使用代理的 attractTo() 方法使粒子朝向或远离特定点移动,该点以屏幕坐标指定。 确切的行为取决于你分配给粒子系统的 attractionStrength 属性的值:正值朝向你的吸引点移动,而负值则远离。
提示
使用 nil 作为参数调用 attractTo() 以清除吸引点。
Vortex 更高级的功能之一是能够创建二级粒子系统——对于一个系统中的每个粒子,创建一个新的粒子系统。 这可以创建多阶段效果,例如烟花:一个粒子向上发射,在飞行时发射火花,然后在死亡时爆炸成彩色。
重要
当创建包含二级系统的粒子系统时,主系统和二级系统都可以拥有自己的标签集。 但是,在创建 ParticleView 时,你必须提供所有系统的所有标签。
VortexSystem 的初始化程序采用各种配置选项来控制你的粒子系统的行为方式。 除一个之外,所有这些都具有合理的默认值,使你可以快速入门并动态调整内容。
VortexSystem 初始化程序的参数为
tags ([String], *必需*) 应该是你传递到 VortexView 以渲染此粒子系统的一个或多个视图的名称。 这个字符串数组可能只是你传入的*某些*视图——例如,你可能有一个使用不同标签的二级系统。secondarySystems ([VortexSystem], 默认为空数组) 应该包含应附加到此主发射器的所有二级粒子系统。spawnOccasion (SpawnOccasion, 默认为 .onBirth) 确定何时应该创建此二级系统。 如果这是你的主粒子系统,则忽略。position (SIMD2<Double>, 默认为 [0.5, 0.5]) 确定此粒子系统的中心位置。shape (Shape, 默认为 .point) 确定粒子发射的位置边界。birthRate (Double, 默认为 100) 确定每秒创建多少个粒子。emissionLimit (Int?, 默认为 nil) 确定此系统在耗尽之前应该创建多少个粒子。emissionDuration (Double, 默认为 1) 确定此粒子系统在暂停之前应发射多长时间。 如果 idleDuration 设置为 0,则不执行任何操作,因为发射之间没有暂停。idleDuration (Double, 默认为 0) 确定发射爆发之间应经过多长时间。burstCount (Int, 默认为 100) 确定当你在此粒子系统的 VortexProxy 上调用 burst() 时应发射多少个粒子。burstCountVariation (Int, 默认为 0) 确定爆发中允许的变化量,+/- 基本 burstCount 值。lifespan (TimeInterval, 默认为 1) 确定粒子在被销毁之前应存活多少秒。lifeSpanVariation (TimeInterval, 默认为 0) 确定粒子寿命允许的变化量,+/- 基本 lifespan 值。speed (Double, 默认为 1) 确定粒子的发射速度。 速度为 1 应允许粒子在 1 秒内从屏幕的一端移动到另一端。speedVariation (Double, 默认为 0) 确定粒子速度允许的变化量,+/- 基本 speed 值。angle (Angle, 默认为 .zero) 确定粒子应发射的方向,其中 0 是直接向上。angleRange (Angle, 默认为 .zero) 确定粒子发射方向允许的变化量,+/- 基本 angle 值。acceleration (SIMD2<Double>, 默认为 [0, 0]) 确定随时间推移调整粒子速度的程度。 正 X 值使粒子向右移动,就像风在吹一样,正 Y 值使粒子向下掉落,就像受到重力影响一样。attractionCenter (SIMD2<Double>?, 默认为 nil) 使粒子朝向或远离特定位置移动。 这应该以屏幕坐标指定。attractionStrength (Double, 默认为 0) 确定朝向或远离 attractionCenter 中指定点的速度。dampingFactor (Double, 默认为 0) 确定粒子随时间推移失去动量的速度。angularSpeed (SIMD3<Double>, 默认为 [0, 0, 0]) 确定粒子应在 X、Y 和 Z 轴上旋转的速度。注意:watchOS 仅支持 Z 旋转。angularSpeedVariation (SIMD3<Double>, 默认为 [0, 0, 0] 确定粒子旋转速度允许的变化量,+/- 基本 angularSpeed 值。colors (ColorMode, 默认为 .single(.white)) 确定粒子应如何随时间推移着色。size (Double, 默认为 1) 确定粒子相对于其源视图的大小,其中 1 是原始大小的 100%。sizeVariation (Double, 默认为 0) 确定初始粒子大小允许的变化量,+/- 基本 size 值。sizeMultiplierAtDeath (Double, 默认为 1) 确定粒子在被销毁时应该变大或变小的程度。 值为 1 表示大小不会改变,而值为 0.5 表示粒子将是其初始大小的一半。stretchFactor (Double, 默认为 1) 确定是否应根据粒子的移动速度拉伸粒子。 值为 1 表示不应用拉伸。其中大多数是内置类型,但有两个值得额外解释。
首先,Shape 允许你从一系列形状发射粒子:单点、直线、圆等等。 例如,这在可用空间中以水平线发射粒子
.box(width: 1, height: 0)
这在可用空间的一半大小的椭圆中创建粒子
.ellipse(radius: 0.5)
其次,ColorMode 使你可以精细地控制颜色如何在 Vortex 中工作。 新粒子系统的默认值为 .single(.white),这意味着所有粒子都是白色创建的。 但是,你可以创建一系列静态颜色的粒子,如下所示
.random(.red, .white, .blue)
你还可以创建颜色渐变,其中粒子会随着年龄的增长而改变颜色。 例如,这使粒子开始为白色,然后变为红色,然后逐渐消失
.ramp(.white, .red, .clear)
为了获得最大的控制,你可以使用*随机渐变*,其中每个粒子系统都会选择一个不同的渐变供粒子在老化时使用。 例如,这使得一些粒子开始为红色然后逐渐消失,而另一些粒子开始为蓝色然后逐渐消失
.randomRamp([.red, .clear], [.blue, .clear])
由于 Vortex 使用这些颜色模式来动态地重新着色你的粒子,因此建议在使用 SwiftUI 的原生形状(例如 Rectangle 和 Circle)时指定 .fill(.white),以确保可以正确地重新着色粒子。
Vortex 提供了一系列内置预设来创建常见效果,也可以作为你自己的创作的起点。
.confetti 预设创建一个彩屑效果,其中视图在爆发发生时飞出。 这意味着使用 VortexViewReader 来访问 Vortex 代理,如下所示
VortexViewReader { proxy in
VortexView(.confetti) {
Rectangle()
.fill(.white)
.frame(width: 16, height: 16)
.tag("square")
Circle()
.fill(.white)
.frame(width: 16)
.tag("circle")
}
Button("Burst", action: proxy.burst)
}
.fire 预设创建一个火焰效果。 当你的粒子具有柔和的边缘并使用 .plusLighter 混合模式时,这会更好,如下所示
VortexView(.fire) {
Circle()
.fill(.white)
.frame(width: 32)
.blur(radius: 3)
.blendMode(.plusLighter)
.tag("circle")
}
.fireflies 预设创建向上放大并逐渐消失的黄色发光点。 当你的粒子具有柔和的边缘时,这会更好,如下所示
VortexView(.fireflies) {
Circle()
.fill(.white)
.frame(width: 32)
.blur(radius: 3)
.blendMode(.plusLighter)
.tag("circle")
}
.fireworks 预设创建一个三阶段粒子效果来模拟爆炸的烟花。 每个烟花都是一个粒子,并且在向上飞行时还会发射新的“火花”粒子。 当烟花粒子被销毁时,它会产生一系列颜色的爆炸效果。
VortexView(.fireworks) {
Circle()
.fill(.white)
.frame(width: 32)
.blur(radius: 5)
.blendMode(.plusLighter)
.tag("circle")
}
.magic 预设创建一个简单的粒子环,这些粒子在逐渐消失时向外飞出。 使用此仓库的 Assets 文件夹中包含的“sparkle”图像效果最佳,但你可以使用任何其他你喜欢的图像或形状。
VortexView(.magic) {
Image(.sparkle)
.blendMode(.plusLighter)
.tag("sparkle")
}
.rain 预设通过根据雨的速度拉伸你的视图来创建降雨系统
VortexView(.rain) {
Circle()
.fill(.white)
.frame(width: 32)
.tag("circle")
}
.smoke 预设创建一个从深灰色到黑色的烟雾效果。 当你的视图稍大一些并且具有柔和的边缘时,这会更好
VortexView(.smoke) {
Circle()
.fill(.white)
.frame(width: 64)
.blur(radius: 10)
.tag("circle")
}
.snow 预设创建一个飘落的雪花效果。 当你的视图具有柔和的边缘时,这会更好,如下所示
VortexView(.snow) {
Circle()
.fill(.white)
.frame(width: 24)
.blur(radius: 5)
.tag("circle")
}
.spark 预设创建一个间歇性的火花效果,其中火花飞出一段时间,然后暂停,然后再次飞出,等等。
VortexView(.spark) {
Circle()
.fill(.white)
.frame(width: 16)
.tag("circle")
}
.splash 预设包含雨滴飞溅效果,就像雨水击打地面一样。它与 .rain 预设结合使用效果最佳,例如:
ZStack {
VortexView(.rain) {
Circle()
.fill(.white)
.frame(width: 32)
.tag("circle")
}
VortexView(.splash) {
Circle()
.fill(.white)
.frame(width: 16, height: 16)
.tag("circle")
}
}
我欢迎所有贡献,无论是添加新的粒子系统预设、修复现有代码、添加注释还是改进此README – 欢迎所有人!
MIT 许可证。
版权所有 (c) 2024 Paul Hudson。
特此授予任何人免费获得本软件及相关文档文件(“软件”)副本的许可,不受限制地处理本软件,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售本软件副本的权利,并允许向其提供本软件的人员这样做,但须符合以下条件:
上述版权声明和本许可声明应包含在本软件的所有副本或主要部分中。
本软件按“原样”提供,不作任何形式的明示或暗示保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任负责,无论是在合同诉讼、侵权行为或其他方面,由本软件或本软件的使用或其他交易引起或与之相关。
Vortex 由 Paul Hudson 制作,他撰写了 Hacking with Swift 上的免费 Swift 教程。它根据 MIT 许可证提供,该许可证允许商业用途、修改、分发和私人用途。