今天改善PositionPanel的一項功能,新增一個變數(lastOffset)儲存上一次的位置,下次重新觸發手勢會從這個位置開始移動。也修正Day2的邏輯錯誤,限制移動的邊界應該是正負100之間才對。
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
}
)
}
}
}
第二部分是把旋轉的控制運算實作出來,概念上很像手遊的虛擬搖桿。先採用印出角度的方式,確保是我預期的效果。
-新增一個RotationPanel
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 maxOffset = 75.0
let newX = max(-maxOffset, min(value.translation.width + lastOffset.width, maxOffset))
let newY = max(-maxOffset, min(value.translation.height + lastOffset.height, maxOffset))
rotationOffset = CGSize(width: newX, height: newY)
}
.onEnded { _ in
lastOffset = rotationOffset
}
)
}
}
}
}
-要觀測移動時的角度變化,在PanelView新增一個Text印出數值
Text("角度: (\(atan2(rotationOffset.width, 75) * 360 / .pi, specifier: "%.2f"), \(atan2(-1*rotationOffset.height, 75) * 360 / .pi, specifier: "%.2f"))")
解釋這邊的算法:想像初始角度為手電筒朝上,這時角度為(0, 0)。RotationPanel的藍點移動到最上方,代表手電筒與畫面呈90度平放,朝螢幕上方,依此類推。這裡還有一處要改善,邊界應該要在圓圈內。
畫面如下圖:
明天的目標是專注在LightZone做出光束的效果,完成後要重構目前的程式碼。
目前有幾個數值都是寫死,遇到不同尺寸裝置會嚴重跑版,也不易維護。