Last active
August 30, 2024 23:52
-
-
Save DarrenHurst/5699108fcb6ed0a867a278147dcb91fe to your computer and use it in GitHub Desktop.
BOO#1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// BooAnimation | |
// | |
// Created by Darren Hurst on 2024-08-30. | |
// | |
import SwiftUI | |
import CoreMotion | |
struct ContentView: View { | |
var body: some View { | |
VStack { | |
BOOView() | |
} | |
.padding() | |
} | |
} | |
#Preview { | |
BOOView() | |
} | |
struct HauntedHouse: View { | |
var body: some View { | |
ZStack { | |
// Main Structure | |
Rectangle() | |
.fill(Color.gray) | |
.frame(width: 250, height: 200) | |
.offset(y: 50) | |
.overlay( | |
Rectangle() | |
.stroke(Color.black, lineWidth: 3) | |
) | |
Rectangle() | |
.frame(width:400,height: 150) | |
.offset(y:60) | |
.opacity(0.8) | |
// Left and Right Side Extensions | |
HStack(spacing: 10) { | |
Rectangle() | |
.fill(Color.gray) | |
.frame(width: 80, height: 180) | |
.overlay( | |
Rectangle() | |
.stroke(Color.black, lineWidth: 3) | |
) | |
Spacer() | |
Rectangle() | |
.fill(Color.gray) | |
.frame(width: 80, height: 180) | |
.overlay( | |
Rectangle() | |
.stroke(Color.black, lineWidth: 3) | |
) | |
} | |
.frame(width: 250) | |
.offset(y: 50) | |
// Roof | |
RoofShape() | |
.fill(Color.black.opacity(0.6)) | |
.frame(width: 300, height: 100) | |
.offset(y: -40) | |
// Left and Right Roof Extensions | |
HStack(spacing: 80) { | |
RoofShape() | |
.fill(Color.gray) | |
.frame(width: 100, height: 50) | |
.offset(x: -110, y: -40) | |
RoofShape() | |
.fill(Color.gray) | |
.frame(width: 100, height: 50) | |
.offset(x: 110, y: -40) | |
} | |
// Door | |
Rectangle() | |
.fill(Color.brown) | |
.frame(width: 60, height: 100) | |
.offset(y: 130) | |
.overlay( | |
Rectangle() | |
.stroke(Color.black, lineWidth: 3) | |
) | |
// Windows | |
VStack(spacing: 10) { | |
HStack(spacing: 50) { | |
WindowShape() | |
WindowShape() | |
} | |
.offset(y: 10) | |
HStack(spacing: 50) { | |
WindowShape() | |
WindowShape() | |
} | |
.offset(y: 50) | |
} | |
ZStack { | |
Circle() | |
.fill(RadialGradient(colors: [.white, .gray, .black], center: .trailing, startRadius: 50, endRadius: 150)) | |
.frame(width: 140) | |
.shadow(color: .white, radius: 10) | |
}.offset(x:-150, y:-250).opacity(0.3) | |
ZStack { | |
Circle() | |
.fill(RadialGradient(colors: [.white, .gray, .black], center: .trailing, startRadius: 50, endRadius: 150)) | |
.frame(width: 140) | |
.shadow(color: .white, radius: 10) | |
Circle().frame(width: 40).shadow(color: .white, radius: 10) | |
}.offset(y:-200) | |
.scaleEffect(0.2) | |
.offset(y:-59) | |
.zIndex(3.0) | |
// Circular Window | |
Circle() | |
.fill(Color.white) | |
.frame(width: 40, height: 40) | |
.overlay( | |
Circle() | |
.stroke(Color.black, lineWidth: 3) | |
) | |
.offset(y: -100) | |
} | |
} | |
} | |
struct RoofShape: Shape { | |
func path(in rect: CGRect) -> Path { | |
var path = Path() | |
path.move(to: CGPoint(x: rect.minX, y: rect.maxY)) | |
path.addLine(to: CGPoint(x: rect.midX, y: rect.minY)) | |
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) | |
path.closeSubpath() | |
return path | |
} | |
} | |
struct WindowShape: View { | |
var body: some View { | |
RoundedRectangle(cornerRadius: 5) | |
.fill(Color.white) | |
.frame(width: 30, height: 50) | |
.overlay( | |
RoundedRectangle(cornerRadius: 5) | |
.stroke(Color.black, lineWidth: 3) | |
) | |
} | |
} | |
struct GhostShape: Shape { | |
func path(in rect: CGRect) -> Path { | |
var path = Path() | |
let width = rect.width | |
let height = rect.height | |
let waveHeight: CGFloat = height * 0.1 | |
let waveWidth: CGFloat = width / 4 | |
// Start at the bottom-left corner | |
path.move(to: CGPoint(x: rect.minX, y: rect.maxY)) | |
// Wavy bottom part | |
path.addCurve(to: CGPoint(x: waveWidth, y: rect.maxY - waveHeight), | |
control1: CGPoint(x: waveWidth / 2, y: rect.maxY), | |
control2: CGPoint(x: waveWidth / 2, y: rect.maxY - waveHeight)) | |
path.addCurve(to: CGPoint(x: 2 * waveWidth, y: rect.maxY), | |
control1: CGPoint(x: waveWidth + waveWidth / 2, y: rect.maxY - waveHeight), | |
control2: CGPoint(x: waveWidth + waveWidth / 2, y: rect.maxY)) | |
path.addCurve(to: CGPoint(x: 3 * waveWidth, y: rect.maxY - waveHeight), | |
control1: CGPoint(x: 2 * waveWidth + waveWidth / 2, y: rect.maxY), | |
control2: CGPoint(x: 2 * waveWidth + waveWidth / 2, y: rect.maxY - waveHeight)) | |
path.addCurve(to: CGPoint(x: rect.maxX, y: rect.maxY), | |
control1: CGPoint(x: 3 * waveWidth + waveWidth / 2, y: rect.maxY - waveHeight), | |
control2: CGPoint(x: 3 * waveWidth + waveWidth / 2, y: rect.maxY)) | |
// Right side straight up | |
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY + height * 0.6)) | |
// Rounded head | |
path.addArc(center: CGPoint(x: width / 2, y: rect.minY + height * 0.6), | |
radius: width / 2, | |
startAngle: .degrees(0), | |
endAngle: .degrees(180), | |
clockwise: true) | |
// Left side straight down | |
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + height * 0.6)) | |
path.closeSubpath() | |
return path | |
} | |
} | |
struct GhostView: View { | |
@State var animate: Bool = false | |
var inColor: Color = .blue | |
@StateObject var boo: BOOSlideValues | |
var body: some View { | |
VStack { | |
let size = (100.0,150.0) | |
ZStack { | |
GhostShape().trim(from: 0, to: 10.0).fill(inColor.opacity(0.4)).shadow(radius: 10) | |
// .offset(x: animate ? -90 : 90).shadow(color: .white, radius: 10) | |
GhostShape().fill(.white.opacity(0.4)).shadow(radius: 10) | |
// .offset(x: animate ? -90 : 90) | |
Circle().fill(.white).frame(width: size.0 / 6, height: size.1 / 3) | |
.offset(x: 9) | |
Circle().fill(.white).frame(width: size.0 / 6, height: size.1 / 3).shadow(radius: 10) | |
Circle().fill(.black).frame(width: size.0 / 12, height: size.1 / 3) | |
.offset(x: 9) | |
Circle().fill(.black).frame(width: size.0 / 12, height: size.1 / 3).shadow(radius: 10) | |
// .offset(x: animate ? -90 : 90) | |
}.frame(width: size.0, height:size.1) | |
.animation(.smooth().speed(0.63).repeatForever(autoreverses: true), value: animate) | |
} | |
.position(x: boo.xpoint, y: boo.ypoint) | |
.onAppear() { | |
animate.toggle() | |
} | |
} | |
} | |
class BOOSlideValues : ObservableObject { | |
@Published var ypoint: Double = 150.0 | |
@Published var xpoint: Double = 150.0 | |
} | |
extension BOOSlideValues: Equatable { | |
static func == (lhs: BOOSlideValues, rhs: BOOSlideValues) -> Bool { | |
rhs.xpoint == lhs.xpoint | |
} | |
} | |
struct BOOView : View { | |
@StateObject var booValues: BOOSlideValues = BOOSlideValues() | |
@State var view: any View = EmptyView() | |
// Detect device shake | |
@State var motionManager:CMMotionManager = CMMotionManager() | |
fileprivate func ghosts(_ r: GeometryProxy, booValues: BOOSlideValues) -> some View { | |
return ZStack { | |
GhostView(inColor: .yellow, boo: booValues) | |
.scaleEffect(0.2) | |
GhostView( boo: booValues) | |
.offset(x:0,y:0) | |
.scaleEffect(0.6) | |
GhostView(inColor: .red, boo: booValues) | |
.offset(x:0,y:150) | |
.scaleEffect(0.9) | |
} | |
.frame(width: r.size.width, height:r.size.height) | |
} | |
var body: some View { | |
motionManager.startDeviceMotionUpdates(to: .main) { motion, error in | |
if let motionx = motion, motionx.rotationRate.x >= 0.5 { | |
print(motionx) | |
booValues.xpoint = booValues.xpoint - 5 | |
if booValues.xpoint < 0 { | |
booValues.xpoint = 0 | |
} | |
} | |
if let motiony = motion, motiony.rotationRate.y >= 0.5 { | |
print(motiony) | |
booValues.ypoint = booValues.ypoint - 5 | |
if booValues.ypoint < 0 { | |
booValues.ypoint = 0 | |
} | |
} | |
if let motionx = motion, motionx.rotationRate.x <= -0.5 { | |
print(motionx) | |
booValues.xpoint = booValues.xpoint + 5 | |
if booValues.xpoint > 320 { | |
booValues.xpoint = 320 | |
} | |
} | |
if let motiony = motion, motiony.rotationRate.y <= -0.5 { | |
print(motiony) | |
booValues.ypoint = booValues.ypoint + 5 | |
if booValues.ypoint > 320 { | |
booValues.ypoint = 320 | |
} | |
} | |
} | |
return VStack { | |
GeometryReader { r in | |
ZStack { | |
HauntedHouse() | |
.onAppear() { | |
view = ghosts(r, booValues: booValues) | |
} | |
} | |
.frame(width: r.size.width, height:r.size.height) | |
.background(LinearGradient(colors: [.black,.white.opacity(0.4),.clear], startPoint: .top, endPoint: .bottom)) | |
.background(LinearGradient(colors: [.clear,.clear ,.green], startPoint: .top, endPoint: .bottom)) | |
AnyView(view) | |
VStack { | |
Slider(value: $booValues.xpoint, in: 0...320, onEditingChanged: { _ in | |
print(booValues.xpoint) | |
view = EmptyView() | |
view = ghosts(r, booValues: booValues) | |
})//.hidden() | |
Slider(value: $booValues.ypoint, in: 0...320, onEditingChanged: { _ in | |
print(booValues.ypoint) | |
view = EmptyView() | |
view = ghosts(r, booValues: booValues) | |
})//.hidden() | |
} | |
} | |
} | |
} | |
} | |
struct GhostPreview : PreviewProvider { | |
static var previews: some View { | |
BOOView() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Simulator.Screen.Recording.-.iPhone.15.-.2024-08-30.at.14.21.48.mp4