Skip to content

Instantly share code, notes, and snippets.

@maiyama18
Created May 29, 2022 02:36
Show Gist options
  • Save maiyama18/82c635ca23701f9e8f77df8e51c75fb2 to your computer and use it in GitHub Desktop.
Save maiyama18/82c635ca23701f9e8f77df8e51c75fb2 to your computer and use it in GitHub Desktop.
import SwiftUI
extension CGPoint {
func distance(to other: CGPoint) -> CGFloat {
let dx = self.x - other.x
let dy = self.y - other.y
return sqrt(dx * dx + dy * dy)
}
}
struct PieChart: Shape {
var progress: Double
func path(in rect: CGRect) -> Path {
Path { path in
let size = min(rect.width, rect.height)
let center = CGPoint(x: rect.width * 0.5, y: rect.height * 0.5)
path.move(to: center)
path.addArc(
center: center,
radius: size * 0.5,
startAngle: Angle(degrees: -90.0),
endAngle: Angle(degrees: -90.0 + 360.0 * progress),
clockwise: false
)
}
}
}
class Model {
private let numPoints = 12000
var positions: [CGPoint] = []
var progress: Double {
Double(positions.count) / Double(numPoints)
}
func reset() {
positions = []
}
func update(date: TimeInterval, size: CGSize) {
if positions.count < numPoints {
for _ in 0..<20 {
let position = randomPosition(size: size)
positions.append(position)
}
}
}
private func randomPosition(size: CGSize) -> CGPoint {
.init(x: Double.random(in: 0...1) * size.width, y: Double.random(in: 0...1) * size.height)
}
}
struct ContentView: View {
private let pointSize = 15.0
private let thresholdDistance = 120.0
private func pointSize(distance: CGFloat) -> CGFloat {
if distance >= thresholdDistance { return 3 }
return 20 + distance * (3 - 20) / thresholdDistance
}
@State private var model: Model = .init()
var body: some View {
TimelineView(.animation) { timeline in
Canvas { context, size in
model.update(date: timeline.date.timeIntervalSince1970, size: size)
context.draw(
context.resolveSymbol(id: 1)!,
at: .init(x: 50, y: 50)
)
context.blendMode = .plusLighter
for position in model.positions {
let pointSize = pointSize(distance: position.distance(to: .init(x: 0.5 * size.width, y: 0.5 * size.height)))
context.fill(
Path(
ellipseIn: .init(
x: position.x - 0.5 * pointSize,
y: position.y - 0.5 * pointSize,
width: pointSize,
height: pointSize
)
),
with: .color(.white.opacity(0.2))
)
}
} symbols: {
PieChart(progress: model.progress)
.fill(.white)
.frame(width: 50, height: 50)
.tag(1)
}
.onTapGesture {
model.reset()
}
}
.ignoresSafeArea()
.background(.black)
}
}
@maiyama18
Copy link
Author

canvas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment