Fill circle with wave animation in SwiftUI

前端 未结 1 1808
走了就别回头了
走了就别回头了 2020-12-16 05:26

I have created a circle in swiftUI, and I want to fill it with sine wave animation for the water wave effect/animation. I wanted to fill it with a similar look:

相关标签:
1条回答
  • 2020-12-16 06:11

    Here's a complete standalone example. It features a slider which allows you to change the percentage:

    import SwiftUI
    
    struct ContentView: View {
        
        @State private var percent = 50.0
        
        var body: some View {
            VStack {
                CircleWaveView(percent: Int(self.percent))
                Slider(value: self.$percent, in: 0...100)
            }
            .padding(.all)
        }
    }
    
    struct Wave: Shape {
    
        var offset: Angle
        var percent: Double
        
        var animatableData: Double {
            get { offset.degrees }
            set { offset = Angle(degrees: newValue) }
        }
        
        func path(in rect: CGRect) -> Path {
            var p = Path()
    
            // empirically determined values for wave to be seen
            // at 0 and 100 percent
            let lowfudge = 0.02
            let highfudge = 0.98
            
            let newpercent = lowfudge + (highfudge - lowfudge) * percent
            let waveHeight = 0.015 * rect.height
            let yoffset = CGFloat(1 - newpercent) * (rect.height - 4 * waveHeight) + 2 * waveHeight
            let startAngle = offset
            let endAngle = offset + Angle(degrees: 360)
            
            p.move(to: CGPoint(x: 0, y: yoffset + waveHeight * CGFloat(sin(offset.radians))))
            
            for angle in stride(from: startAngle.degrees, through: endAngle.degrees, by: 5) {
                let x = CGFloat((angle - startAngle.degrees) / 360) * rect.width
                p.addLine(to: CGPoint(x: x, y: yoffset + waveHeight * CGFloat(sin(Angle(degrees: angle).radians))))
            }
            
            p.addLine(to: CGPoint(x: rect.width, y: rect.height))
            p.addLine(to: CGPoint(x: 0, y: rect.height))
            p.closeSubpath()
            
            return p
        }
    }
    
    struct CircleWaveView: View {
        
        @State private var waveOffset = Angle(degrees: 0)
        let percent: Int
        
        var body: some View {
    
            GeometryReader { geo in
                ZStack {
                    Text("\(self.percent)%")
                        .foregroundColor(.black)
                        .font(Font.system(size: 0.25 * min(geo.size.width, geo.size.height) ))
                    Circle()
                        .stroke(Color.blue, lineWidth: 0.025 * min(geo.size.width, geo.size.height))
                        .overlay(
                            Wave(offset: Angle(degrees: self.waveOffset.degrees), percent: Double(percent)/100)
                                .fill(Color(red: 0, green: 0.5, blue: 0.75, opacity: 0.5))
                                .clipShape(Circle().scale(0.92))
                        )
                }
            }
            .aspectRatio(1, contentMode: .fit)
            .onAppear {
                withAnimation(Animation.linear(duration: 2).repeatForever(autoreverses: false)) {
                self.waveOffset = Angle(degrees: 360)
                }
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            CircleWaveView(percent: 58)
        }
    }
    
    0 讨论(0)
提交回复
热议问题