Skip to content

Instantly share code, notes, and snippets.

@mattyoung
Created December 27, 2022 20:06
Show Gist options
  • Save mattyoung/c45effa26ac027d346f8724da5ee1cb9 to your computer and use it in GitHub Desktop.
Save mattyoung/c45effa26ac027d346f8724da5ee1cb9 to your computer and use it in GitHub Desktop.
//
// EllipticalOrbitView.swift
// ChristmasTreeConclusion
//
// Created by Matthew Young on 12/27/22.
//
import SwiftUI
enum Elliptical {
/// Calculate the cartitian coordinate (x, y)
/// - Parameters:
/// - angle: at this angle
/// - bounds: ellipse bounds
/// - Returns: the coordinate in a tuple
static func coordinate(angle: Angle, bounds: CGSize) -> (x: Double, y: Double) {
(x: bounds.width / 2 + bounds.width / 2 * cos(angle.radians), y: bounds.height / 2 + bounds.height / 2 * sin(angle.radians))
}
}
struct EllipticalOrbitView<Content: View>: View {
let angle: Angle
@ViewBuilder let content: () -> Content
@State private var contentSize = CGSize.zero
@ViewBuilder
func dot(proxy: GeometryProxy) -> some View {
let (x, y) = Elliptical.coordinate(angle: angle, bounds: proxy.size)
content()
.switchingColor(period: 1, onChange: Animation.linear, colors: Color.someColors)
.offset(x: x - contentSize.width / 2, y: y - contentSize.height / 2)
.animation(.linear, value: angle)
.readSize($into: $contentSize)
}
var body: some View {
GeometryReader { proxy in
dot(proxy: proxy)
}
}
}
struct SelfRotatingEllipticalOrbitView<Content: View>: View {
let offset: Angle
let content: Content
init(angle offset: Angle = .zero, @ViewBuilder content: () -> Content) {
self.offset = offset
self.content = content()
}
var body: some View {
TimelineView(.animation) { context in
EllipticalOrbitView(angle: Angle.degrees(context.date.angle.degrees + offset.degrees)) {
content
}
}
}
}
struct EllipticalOrbitingEmojis: View {
let emojis: [Text]
init(emojis: String = "πŸŽ‰πŸŽŠπŸŽˆπŸŽ†πŸŽ‡πŸŽƒπŸŽ‚πŸŽπŸŽ‘πŸŽπŸŽπŸŽŽπŸŽπŸŽŒπŸŽ‰πŸŽŠπŸŽˆπŸŽ†πŸŽ‡πŸŽƒπŸŽ‚πŸŽπŸŽ‘πŸŽπŸŽπŸŽŽπŸŽπŸŽŒ") {
assert(!emojis.isEmpty)
self.emojis = emojis.map({ Text(String($0))})
}
var body: some View {
let angleSize = 360.0 / Double(emojis.count)
Color.clear
.overlay {
ForEach(emojis.indices, id: \.self) { index in
SelfRotatingEllipticalOrbitView(angle: .degrees(Double(index) * angleSize)) {
emojis[index]
.font(.system(size: 50))
.frame(width: 50, height: 50)
.foregroundColor(.teal)
}
}
}
}
}
struct SelfRotatingEllipticalOrbitViewDemo: View {
let emoji = Text(String("πŸŽ‰πŸŽŠπŸŽˆπŸŽ†πŸŽ‡πŸŽƒπŸŽ‚πŸŽπŸŽ‘πŸŽπŸŽπŸŽŽπŸŽπŸŽŒπŸŽ‰πŸŽŠπŸŽˆπŸŽ†πŸŽ‡πŸŽƒπŸŽ‚πŸŽπŸŽ‘πŸŽπŸŽπŸŽŽπŸŽπŸŽŒ".randomElement()!))
var body: some View {
VStack {
EllipticalOrbitingEmojis()
.background {
let colors = Color.rainbowColors.shuffled()
Ellipse()
.strokeBorder(.angularGradient(colors: colors + [colors.first!], center: .center, startAngle: .degrees(0.0), endAngle: .degrees(360)), lineWidth: 15)
}
.border(.green)
HStack{
Color.clear
.overlay {
ForEach(0...12, id: \.self) { index in
SelfRotatingEllipticalOrbitView(angle: .degrees(Double(index) * 360 / 12)) {
Text(String("πŸŽ‰πŸŽŠπŸŽˆπŸŽ†πŸŽ‡πŸŽƒπŸŽ‚πŸŽπŸŽ‘πŸŽπŸŽπŸŽŽπŸŽπŸŽŒπŸŽ‰πŸŽŠπŸŽˆπŸŽ†πŸŽ‡πŸŽƒπŸŽ‚πŸŽπŸŽ‘πŸŽπŸŽπŸŽŽπŸŽπŸŽŒ".randomElement()!))
.font(.system(size: 50))
.frame(width: 50, height: 50)
.foregroundColor(.teal)
}
}
}
SelfRotatingEllipticalOrbitView(angle: .degrees(30)) {
Circle()
.frame(width: 50, height: 50)
}
.background {
let colors = Color.rainbowColors.shuffled()
Ellipse()
.strokeBorder(.angularGradient(colors: colors + [colors.first!], center: .center, startAngle: .degrees(0.0), endAngle: .degrees(360)), lineWidth: 15)
}
.border(.red)
SelfRotatingEllipticalOrbitView(angle: .degrees(60)) {
Circle()
.frame(width: 50, height: 50)
}
.background {
let colors = Color.rainbowColors.shuffled()
Ellipse()
.strokeBorder(.angularGradient(colors: colors + [colors.first!], center: .center, startAngle: .degrees(0.0), endAngle: .degrees(360)), lineWidth: 15)
}
.border(.red)
}
Ring(dotSize: 50)
Ring(dotSize: 150)
}
}
}
struct SelfRotatingEllipticalOrbitViewDemo_Previews: PreviewProvider {
static var previews: some View {
SelfRotatingEllipticalOrbitViewDemo()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment