60 lines
2.3 KiB
Swift
60 lines
2.3 KiB
Swift
//
|
|
// 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)
|
|
}
|
|
}
|