// // CircularSliderView.swift // fiveConstant // // Created by 李建 on 2023/2/6. // // 弧形温度调节组件 import SwiftUI struct CircularSliderView: View { @State var roomInfo: RoomInfo @State var temperatureValue: CGFloat @State var angleValue: CGFloat var onSetValue: ((Int)->Void)? let config = Config(minimumValue: 0, maximumValue: 14, totalValue: 14.0, knobRadius: 18.0, radius: 105.0) init(roomInfo: RoomInfo) { self.roomInfo = roomInfo let value = CGFloat(roomInfo.set_temp!) self.temperatureValue = value let fixAngle = (value - 16) / config.totalValue * (270 * .pi / 180) self.angleValue = fixAngle * 180 / .pi } var body: some View { ZStack { Circle() .trim(from: 0.125, to: 0.875) .stroke(roomInfo.power! ? Color("MainColor") : Color(hex: 0xF0F5F6, alpha: 1), lineWidth: 20) .frame(width: config.radius * 2, height: config.radius * 2) .rotationEffect(.degrees(90)) ZStack { Circle() .fill(Color.white) .frame(width: config.knobRadius * 2, height: config.knobRadius * 2) .padding(10) .shadow(radius: 11, y: 5) .offset(y: -config.radius) .rotationEffect(Angle.degrees(angleValue)) .zIndex(5) .gesture(DragGesture(minimumDistance: 0.0) .onChanged({ value in change(location: value.location) }) .onEnded({ _ in self.onSetValue!(Int(temperatureValue)) }) ) Circle() .fill(Color("MainColor")) .frame(width: 12, height: 12) .offset(y: -config.radius) .zIndex(6) .rotationEffect(Angle.degrees(angleValue)) // 两头的小圆点 Circle() .fill(roomInfo.power! ? Color("MainColor") : Color(hex: 0xF0F5F6, alpha: 1)) .frame(width: 19, height: 19) .offset(y: -config.radius) Circle() .fill(roomInfo.power! ? Color("MainColor") : Color(hex: 0xF0F5F6, alpha: 1)) .frame(width: 19, height: 19) .offset(y: -config.radius) .rotationEffect(Angle.degrees(270)) } .rotationEffect(.degrees(225)) // HStack(spacing:0) { Text("\(String.init(format: "%.f", temperatureValue))") .font(.system(size: 60)) .foregroundColor(Color("SecondColor")) .bold() Text("℃").font(.system(size: 20)).padding(.bottom, 30) } } } private func change(location: CGPoint) { let vector = CGVector(dx: location.x, dy: location.y) let angle = atan2(vector.dy - (config.knobRadius + 10), vector.dx - (config.knobRadius + 10)) + .pi/2.0 let fixedAngle = angle < 0.0 ? angle + 2.0 * .pi : angle let value = fixedAngle / (270 * .pi / 180) * config.totalValue if value >= config.minimumValue && value <= config.maximumValue { temperatureValue = value + 16 angleValue = fixedAngle * 180 / .pi } } } extension CircularSliderView { func onDragEnd(perform action: @escaping (Int)->Void) ->Self { var copy = self copy.onSetValue = action return copy } } struct Config { let minimumValue: CGFloat let maximumValue: CGFloat let totalValue: CGFloat let knobRadius: CGFloat let radius: CGFloat } struct CircularSliderView_Previews: PreviewProvider { static let json = """ {"record_id":"1t7svi03170copjp9iimtd3z005isbfj","name":"样板间","home_id":"1t7svi01jj7copaplj7q47n170kd6hw6","home_name":"李建的家","is_master":true,"control_number":"1","user_id":"","power":true,"set_temp":21,"air_quality":0,"co2":0,"temperature":7,"humidity":0,"mode":2,"fan_speed":4,"fan_value":1,"timer_status":false,"duration":0} """.data(using: .utf8)! static var previews: some View { let decoder = JSONDecoder() let product = try? decoder.decode(RoomInfo.self, from: json) CircularSliderView(roomInfo: product!) } }