룰렛 뷰 추가
This commit is contained in:
59
SodaLive/Sources/FortuneWheel/FortuneWheelViewModel.swift
Normal file
59
SodaLive/Sources/FortuneWheel/FortuneWheelViewModel.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// FortuneWheelViewModel.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2023/12/07.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
class FortuneWheelViewModel: ObservableObject {
|
||||
|
||||
private var pendingRequestWorkItem: DispatchWorkItem?
|
||||
@Published var degree = 0.0
|
||||
|
||||
private let model: FortuneWheelModel
|
||||
|
||||
init(model: FortuneWheelModel) {
|
||||
self.model = model
|
||||
}
|
||||
|
||||
private func getWheelStopDegree() -> Double {
|
||||
var index = -1;
|
||||
if let method = model.getWheelItemIndex { index = method() }
|
||||
if index < 0 || index >= model.titles.count { index = Int.random(in: 0..<model.titles.count) }
|
||||
index = model.titles.count - index - 1;
|
||||
/*
|
||||
itemRange - Each items degree range (For 4, each will have 360 / 4 = 90 degrees)
|
||||
indexDegree - No. of 90 degrees to reach i item
|
||||
freeRange - Flexible degree in the item, so the pointer doesn't always point start of the item
|
||||
freeSpins - No. of spins before it goes to selected item index
|
||||
finalDegree - Final exact degree to spin and stop in the index
|
||||
*/
|
||||
let itemRange = 360 / model.titles.count;
|
||||
let indexDegree = itemRange * index;
|
||||
let freeRange = Int.random(in: 0...itemRange);
|
||||
let freeSpins = (2...20).map({ return $0 * 360 }).randomElement()!
|
||||
let finalDegree = freeSpins + indexDegree + freeRange;
|
||||
return Double(finalDegree);
|
||||
}
|
||||
|
||||
func spinWheel() {
|
||||
withAnimation(model.animation) {
|
||||
self.degree = Double(360 * Int(self.degree / 360)) + getWheelStopDegree();
|
||||
}
|
||||
// Cancel the currently pending item
|
||||
pendingRequestWorkItem?.cancel()
|
||||
// Wrap our request in a work item
|
||||
let requestWorkItem = DispatchWorkItem { [weak self] in
|
||||
guard let self = self else { return }
|
||||
let count = self.model.titles.count
|
||||
let distance = self.degree.truncatingRemainder(dividingBy: 360)
|
||||
let pointer = floor(distance / (360 / Double(count)))
|
||||
if let onSpinEnd = self.model.onSpinEnd { onSpinEnd(count - Int(pointer) - 1) }
|
||||
}
|
||||
// Save the new work item and execute it after duration
|
||||
pendingRequestWorkItem = requestWorkItem
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + model.animDuration + 1, execute: requestWorkItem)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user