今天拆分成不同檔案,建立LightSettings存放操作變數。
LightSettings.swift
import SwiftUI
struct LightSettings {
var circleSize: CGFloat
var brightness: Double = 1.0
var color: Color
var offset: CGSize
var rotationOffset: CGSize
func getAngelX() -> CGFloat {
return -1 * atan2(self.rotationOffset.width, 75) * 360 / .pi
}
func getAngelY() -> CGFloat {
return atan2(self.rotationOffset.height, 75) * 360 / .pi
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
@State private var lightSettings = LightSettings(
circleSize:CGFloat(50),
brightness: 1.0,
color: .white,
offset: .zero,
rotationOffset: .zero
)
var body: some View {
VStack {
LightZone(lightSettings: lightSettings)
.frame(maxWidth: .infinity)
PanelView(lightSettings: $lightSettings)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
LightZone.swift
光線效果嘗試了好幾個做法,最後發現RadialGradient最適合模擬光源效果的邊緣漸層。
rotation3DEffect能滿足我要的需求,個別對3軸做出變形效果。
還想要改善大角度時的光束效果,應該要有一個Slider可設定光束長短,下階段安排進清單。
import SwiftUI
struct LightZone: View {
let scale = UIScreen.main.bounds.width/200.0
let lightSettings: LightSettings
let blurRate = 0.2
var body: some View {
ZStack {
Color.black
.ignoresSafeArea()
Circle()
.fill(
RadialGradient(
gradient: Gradient(colors: [lightSettings.color.opacity(lightSettings.brightness), .clear]),
center: .center,
startRadius: lightSettings.circleSize*0.15,
endRadius: lightSettings.circleSize*0.5
)
)
.frame(width: lightSettings.circleSize, height: lightSettings.circleSize)
.rotation3DEffect(.degrees(lightSettings.getAngelY()),axis: (x: 1, y: 0, z: 0))
.rotation3DEffect(.degrees(lightSettings.getAngelX()), axis: (x: 0, y: 1, z: 0))
.offset(CGSize(width: lightSettings.offset.width*scale, height: lightSettings.offset.height*scale))
}
}
}
*PanelView.swift *
這裡還有x,y軸定義混亂的問題待修正(考量截稿時間優先)
轉動角度應該控制在60度以內就夠了。
import SwiftUI
struct PanelView: View {
@Binding var lightSettings: LightSettings
var body: some View {
VStack {
Slider(value: $lightSettings.circleSize, in: 20...80)
Text("光圈大小: \(Int(lightSettings.circleSize))")
Slider(value: $lightSettings.brightness, in: 0.5...1)
Text("亮度: \(lightSettings.brightness, format: .percent)")
ColorPicker("顏色選擇", selection: $lightSettings.color)
Text("角度: (\(-1*lightSettings.getAngelX(), specifier: "%.2f"), \(-1*lightSettings.getAngelY(), specifier: "%.2f"))")
HStack {
RotationPanel(rotationOffset: $lightSettings.rotationOffset)
.frame(width: 150, height: 150)
PositionPanel(offset: $lightSettings.offset)
.frame(width: 200, height: 200)
}
}
.padding()
}
}
struct PositionPanel: View {
@Binding var offset: CGSize
@State private var lastOffset: CGSize = .zero
var body: some View {
ZStack {
Rectangle()
.stroke(Color.green, lineWidth: 2)
MovableCircle()
.offset(offset)
.gesture(
DragGesture()
.onChanged { value in
let maxOffset = 100.0
let newX = max(-maxOffset, min(value.translation.width + lastOffset.width, maxOffset))
let newY = max(-maxOffset, min(value.translation.height + lastOffset.height, maxOffset))
offset = CGSize(width: newX, height: newY)
}
.onEnded { _ in
lastOffset = offset
}
)
}
}
}
struct RotationPanel: View {
@Binding var rotationOffset: CGSize
@State private var lastOffset: CGSize = .zero
var body: some View {
GeometryReader { geometry in
ZStack {
Circle()
.stroke(Color.gray, lineWidth: 2)
.frame(width: 150, height: 150)
Circle()
.fill(Color.blue)
.frame(width: 20, height: 20)
.offset(rotationOffset)
.gesture(
DragGesture()
.onChanged { value in
let radius = 75.0 // 圓形的半徑
var newX = value.translation.width + lastOffset.width
var newY = value.translation.height + lastOffset.height
let distance = sqrt(newX * newX + newY * newY)
if distance > radius {
let angle = Double(atan2(newY, newX))
newX = radius * cos(angle)
newY = radius * sin(angle)
}
rotationOffset = CGSize(width: newX, height: newY)
}
.onEnded { _ in
lastOffset = rotationOffset
}
)
}
}
}
}
struct MovableCircle: View {
var body: some View {
Circle()
.fill(Color.red)
.frame(width: 20, height: 20)
}
}
看看模擬器效果如何
第一階段試驗告一段落,下一步是盤點上架之前要加入的小功能。
趁著週末有滿滿的時間,我會先寫一篇關於元件Layout的題材。
APP要支援不同尺寸裝置,以及直向與橫向。
一轉眼已經Day6,期許接下來內容更豐富一些。
摸索SwiftUI一段,多虧新的AI工具Gemini加速學習過程,繼續做下去,迎接新挑戰吧!