Skip to content

Instantly share code, notes, and snippets.

@kierancrown
Created March 9, 2022 14:53
Show Gist options
  • Save kierancrown/bf83669a7f301bd80cde136fd53bef6a to your computer and use it in GitHub Desktop.
Save kierancrown/bf83669a7f301bd80cde136fd53bef6a to your computer and use it in GitHub Desktop.
Confetti Animation in SwiftUI
import Foundation
import SwiftUI
enum ConfettiShape {
case rectangle
case circle
}
enum ConfettiPosition {
case foreground
case background
}
class ConfettiType {
let color: UIColor
let shape: ConfettiShape
let position: ConfettiPosition
init(color: UIColor, shape: ConfettiShape, position: ConfettiPosition) {
self.color = color
self.shape = shape
self.position = position
}
lazy var name = UUID().uuidString
lazy var image: UIImage = {
let imageRect: CGRect = {
switch shape {
case .rectangle:
return CGRect(x: 0, y: 0, width: 20, height: 13)
case .circle:
return CGRect(x: 0, y: 0, width: 10, height: 10)
}
}()
UIGraphicsBeginImageContext(imageRect.size)
let context = UIGraphicsGetCurrentContext()!
context.setFillColor(color.cgColor)
switch shape {
case .rectangle:
context.fill(imageRect)
case .circle:
context.fillEllipse(in: imageRect)
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}()
}
var confettiTypes: [ConfettiType] = {
let confettiColors = [
(r:149,g:58,b:255), (r:255,g:195,b:41), (r:255,g:101,b:26),
(r:123,g:92,b:255), (r:76,g:126,b:255), (r:71,g:192,b:255),
(r:255,g:47,b:39), (r:255,g:91,b:134), (r:233,g:122,b:208)
].map { UIColor(red: $0.r / 255.0, green: $0.g / 255.0, blue: $0.b / 255.0, alpha: 1) }
// For each position x shape x color, construct an image
return [ConfettiPosition.foreground, ConfettiPosition.background].flatMap { position in
return [ConfettiShape.rectangle, ConfettiShape.circle].flatMap { shape in
return confettiColors.map { color in
return ConfettiType(color: color, shape: shape, position: position)
}
}
}
}()
func createConfettiCells() -> [CAEmitterCell] {
return confettiTypes.map { confettiType in
let cell = CAEmitterCell()
cell.name = confettiType.name
cell.beginTime = 0.1
cell.birthRate = 100
cell.contents = confettiType.image.cgImage
cell.emissionRange = CGFloat(Double.pi)
cell.lifetime = 10
cell.spin = 4
cell.spinRange = 8
cell.velocityRange = 0
cell.yAcceleration = 0
// Step 3: A _New_ Spin On Things
cell.setValue("plane", forKey: "particleType")
cell.setValue(Double.pi, forKey: "orientationRange")
cell.setValue(Double.pi / 2, forKey: "orientationLongitude")
cell.setValue(Double.pi / 2, forKey: "orientationLatitude")
return cell
}
}
struct ConfettiView: UIViewRepresentable {
let color: UIColor
let shape: ConfettiShape
let position: ConfettiPosition
let bounds: CGRect
func makeUIView(context: Context) -> some UIView {
let view = UIView()
/*
This is the layer that contains the confetti emitter
*/
lazy var confettiLayer: CAEmitterLayer = {
let emitterLayer = CAEmitterLayer()
emitterLayer.emitterCells = confettiCells
emitterLayer.emitterPosition = CGPoint(x: bounds.midX, y: bounds.minY - 500)
emitterLayer.emitterSize = CGSize(width: bounds.size.width, height: 500)
emitterLayer.emitterShape = .rectangle
emitterLayer.frame = bounds //view.bounds
emitterLayer.beginTime = CACurrentMediaTime()
return emitterLayer
}()
/*
This is the actual singular confetti emitter cell
*/
lazy var confettiCells: [CAEmitterCell] = {
return confettiTypes.map { confettiType in
let cell = CAEmitterCell()
cell.setValue("plane", forKey: "particleType")
cell.setValue(Double.pi, forKey: "orientationRange")
cell.setValue(Double.pi / 2, forKey: "orientationLongitude")
cell.setValue(Double.pi / 2, forKey: "orientationLatitude")
cell.beginTime = 0.1
cell.birthRate = 4
cell.contents = confettiType.image.cgImage
cell.emissionRange = CGFloat(Double.pi)
cell.lifetime = 10
cell.spin = 4
cell.spinRange = 5
cell.velocityRange = 100
cell.yAcceleration = 88
return cell
}
}()
view.layer.addSublayer(confettiLayer)
view.bounds = bounds
view.isUserInteractionEnabled = false
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment