diff --git a/SodaLive/Sources/FortuneWheel/FortuneWheel.swift b/SodaLive/Sources/FortuneWheel/FortuneWheel.swift new file mode 100644 index 0000000..31e76e4 --- /dev/null +++ b/SodaLive/Sources/FortuneWheel/FortuneWheel.swift @@ -0,0 +1,40 @@ +// +// FortuneWheel.swift +// SodaLive +// +// Created by klaus on 2023/12/07. +// + +import SwiftUI + +struct FortuneWheel: View { + + private let model: FortuneWheelModel + @StateObject private var viewModel: FortuneWheelViewModel + + public init(model: FortuneWheelModel) { + self.model = model + _viewModel = StateObject(wrappedValue: FortuneWheelViewModel(model: model)) + } + + public var body: some View { + ZStack(alignment: .top) { + ZStack(alignment: .center) { + SpinWheelView(data: (0.. ())? + let colors: [Color] + let pointerColor: Color + let strokeWidth: CGFloat + let strokeColor: Color + let animDuration: Double + let animation: Animation + let getWheelItemIndex: (() -> (Int))? + + public init( + titles: [String], size: CGFloat, onSpinEnd: ((Int) -> ())?, + colors: [Color]? = nil, + pointerColor: Color = .red, + strokeWidth: CGFloat = 5, + strokeColor: Color = .white, + animDuration: Double = Double(2), + animation: Animation? = nil, + getWheelItemIndex: (() -> (Int))? = nil + ) { + self.titles = titles + self.size = size + self.onSpinEnd = onSpinEnd + self.colors = colors ?? [ + Color(hex: "#F5D55A"), + Color(hex: "#E4813B"), + Color(hex: "#E6AAC1"), + Color(hex: "#8FCEEA"), + Color(hex: "#CD5880"), + Color(hex: "#C2C85E") + ] + self.pointerColor = pointerColor + self.strokeWidth = strokeWidth + self.strokeColor = strokeColor + self.animDuration = animDuration + self.animation = animation ?? Animation.timingCurve(0.51, 0.97, 0.56, 0.99, duration: animDuration) + self.getWheelItemIndex = getWheelItemIndex + } +} diff --git a/SodaLive/Sources/FortuneWheel/FortuneWheelViewModel.swift b/SodaLive/Sources/FortuneWheel/FortuneWheelViewModel.swift new file mode 100644 index 0000000..3e58696 --- /dev/null +++ b/SodaLive/Sources/FortuneWheel/FortuneWheelViewModel.swift @@ -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.. Path { + var path = Path() + let radius = min(rect.width, rect.height) / 2 + let alpha = CGFloat(startAngle) + let center = CGPoint( + x: rect.midX, + y: rect.midY + ) + path.move(to: center) + path.addLine( + to: CGPoint( + x: center.x + cos(alpha) * radius, + y: center.y + sin(alpha) * radius + ) + ) + path.addArc( + center: center, radius: radius, + startAngle: Angle(radians: startAngle), + endAngle: Angle(radians: endAngle), + clockwise: false + ) + path.closeSubpath() + return path + } +} diff --git a/SodaLive/Sources/FortuneWheel/SpinWheelView.swift b/SodaLive/Sources/FortuneWheel/SpinWheelView.swift new file mode 100644 index 0000000..3cf3cf8 --- /dev/null +++ b/SodaLive/Sources/FortuneWheel/SpinWheelView.swift @@ -0,0 +1,96 @@ +// +// SpinWheelView.swift +// SodaLive +// +// Created by klaus on 2023/12/07. +// + +import SwiftUI + +struct Triangle: Shape { + public func path(in rect: CGRect) -> Path { + var path = Path() + path.move(to: CGPoint(x: rect.midX, y: rect.minY)) + path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) + path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) + path.addLine(to: CGPoint(x: rect.midX, y: rect.minY)) + path.addCurve(to: CGPoint(x: rect.midX, y: rect.minY), control1: CGPoint(x: rect.maxX, y: rect.minY), control2: CGPoint(x: rect.midX, y: rect.minY)) + return path + } +} + +struct SpinWheelPointer: View { + var pointerColor: Color + var body: some View { + Triangle().frame(width: 50, height: 50) + .foregroundColor(pointerColor).cornerRadius(24) + .rotationEffect(.init(degrees: 180)) + .shadow(color: Color(hex: "212121").opacity(0.5), radius: 5, x: 0.0, y: 1.0) + } +} + +struct SpinWheelBolt: View { + var body: some View { + ZStack { + Circle().frame(width: 28, height: 28) + .foregroundColor(Color(hex: "F4C25B")) + Circle().frame(width: 18, height: 18) + .foregroundColor(Color(hex: "FFD25A")) + .shadow(color: Color(hex: "404040").opacity(0.35), radius: 3, x: 0.0, y: 1.0) + } + } +} + +struct SpinWheelView: View { + + var data: [Double], labels: [String] + + private let colors: [Color] + private let sliceOffset: Double = -.pi / 2 + @available(macOS 10.15, *) + + init(data: [Double], labels: [String], colors: [Color]) { + self.data = data + self.labels = labels + self.colors = colors + } + @available(macOS 10.15.0, *) + + var body: some View { + GeometryReader { geo in + ZStack(alignment: .center) { + ForEach(0.. Double { + switch index { + case 0: return sliceOffset + default: + let ratio: Double = data[.. Double { + switch index { + case data.count - 1: return sliceOffset + 2 * .pi + default: + let ratio: Double = data[..<(index + 1)].reduce(0.0, +) / data.reduce(0.0, +) + return sliceOffset + 2 * .pi * ratio + } + } + + private func viewOffset(for index: Int, in size: CGSize) -> CGSize { + let radius = min(size.width, size.height) / 3 + let dataRatio = (2 * data[.. 0 ? .bottom : .init()) - .transaction { transaction in - transaction.animation = nil - } .sheet( isPresented: $viewModel.isShowShareView, onDismiss: { viewModel.shareMessage = "" }, diff --git a/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift b/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift index 212b472..03c0971 100644 --- a/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift +++ b/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift @@ -141,7 +141,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject { @Published var roulettePreview: RoulettePreview? = nil @Published var isShowRoulette = false - @Published var rouletteItems = [RouletteItem]() + @Published var rouletteItems = [String]() @Published var rouletteSelectedItem = "" var rouletteCan = 0 @@ -1432,10 +1432,9 @@ final class LiveRoomViewModel: NSObject, ObservableObject { } func sendRouletteDonation() { - let rawMessage = rouletteSelectedItem let rouletteRawMessage = LiveRoomChatRawMessage( type: .ROULETTE_DONATION, - message: rawMessage, + message: rouletteSelectedItem, can: rouletteCan, donationMessage: "" ) @@ -1449,7 +1448,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject { LiveRoomRouletteDonationChat( profileUrl: profileUrl, nickname: nickname, - rouletteResult: rawMessage + rouletteResult: rouletteSelectedItem ) ) @@ -1495,10 +1494,10 @@ final class LiveRoomViewModel: NSObject, ObservableObject { isLoading = false self.rouletteItems.removeAll() - self.rouletteItems.append(contentsOf: items) + self.rouletteItems.append(contentsOf: items.map { $0.title }) self.rouletteSelectedItem = rouletteItems[Int(arc4random_uniform(UInt32(rouletteItems.count)))] self.rouletteCan = can - sendRouletteDonation() + self.isShowRoulette = true } private func refundRouletteDonation() { diff --git a/SodaLive/Sources/Live/Room/Routlette/RouletteViewDialog.swift b/SodaLive/Sources/Live/Room/Routlette/RouletteViewDialog.swift new file mode 100644 index 0000000..e2b3281 --- /dev/null +++ b/SodaLive/Sources/Live/Room/Routlette/RouletteViewDialog.swift @@ -0,0 +1,37 @@ +// +// RouletteViewDialog.swift +// SodaLive +// +// Created by klaus on 2023/12/07. +// + +import SwiftUI + +struct RouletteViewDialog: View { + @Binding var isShowing: Bool + + let options: [String] + let selectedOption: String + let complete: () -> Void + + var body: some View { + let model = FortuneWheelModel( + titles: options, + size: 320, + onSpinEnd: onSpinEnd, + getWheelItemIndex: getWheelItemIndex + ) + ZStack { + FortuneWheel(model: model) + } + } + + private func onSpinEnd(index: Int) { + complete() + isShowing = false + } + + private func getWheelItemIndex() -> Int { + return options.firstIndex(of: selectedOption) ?? 0 + } +}