ios SwiftUI涟漪效果动画

toiithl6  于 2022-11-26  发布在  iOS
关注(0)|答案(2)|浏览(156)

我正在SwiftUI中创建一个类似于here的涟漪效果。
以下是我目前所掌握的情况:

import SwiftUI

// MARK: - Ripple

struct Ripple: ViewModifier {
    // MARK: Lifecycle

    init(rippleColor: Color) {
        self.rippleColor = rippleColor
    }

    // MARK: Internal

    let rippleColor: Color

    func body(content: Content) -> some View {
        ZStack {
            content

            if let location = touchPoint {
                Circle()
                    .fill(rippleColor)
                    .frame(width: 16.0, height: 16.0)
                    .position(location)
                    .clipped()
                    .opacity(opacity)
            }
        }
        .fixedSize()
        .gesture(
            DragGesture(minimumDistance: 0.0)
                .onChanged { gesture in
                    guard touchPoint != gesture.startLocation else {
                        return
                    }

                    timer?.invalidate()

                    opacity = 1.0
                    touchPoint = gesture.startLocation
                }
                .onEnded { _ in
                    timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { _ in
                        withAnimation {
                            opacity = 0.0
                        }
                    }
                }
        )
    }

    // MARK: Private

    @State private var opacity: CGFloat = 0.0
    @State private var touchPoint: CGPoint?
    @State private var timer: Timer?
}

extension View {
    func rippleEffect(rippleColor: Color = .accentColor.opacity(0.5)) -> some View {
        modifier(Ripple(rippleColor: rippleColor))
    }
}

下一步是做缩放动画,但我很难弄清楚如何做。我试过用缩放修改器应用缩放效果和过渡,但似乎没有什么能正常工作。
有人能帮助我实现我所寻找的连锁React吗?
另外,如果已经存在这样的东西,我很乐意使用它,但我还没有找到任何东西。
谢谢你,
RPK公司

rjzwgtxy

rjzwgtxy1#

您可能正在寻找这样的东西...

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
        .rippleEffect(rippleColor: .gray)
        .frame(width: 400, height: 200)
        .padding()
    }
}

struct Ripple: ViewModifier {
    // MARK: Lifecycle

    init(rippleColor: Color) {
        self.color = rippleColor
    }

    // MARK: Internal

    let color: Color

    @State private var scale: CGFloat = 0.5
    
    @State private var animationPosition: CGFloat = 0.0
    @State private var x: CGFloat = 0.0
    @State private var y: CGFloat = 0.0
    
    @State private var opacityFraction: CGFloat = 0.0
    
    let timeInterval: TimeInterval = 0.5
    
    func body(content: Content) -> some View {
        GeometryReader { geometry in
            ZStack {
                Rectangle()
                    .foregroundColor(.gray.opacity(0.05))
                Circle()
                    .foregroundColor(color)
                    .opacity(0.2*opacityFraction)
                    .scaleEffect(scale)
                    .offset(x: x, y: y)
                content
            }
            .onTapGesture(perform: { location in
                x = location.x-geometry.size.width/2
                y = location.y-geometry.size.height/2
                opacityFraction = 1.0
                withAnimation(.linear(duration: timeInterval)) {
                    scale = 3.0*(max(geometry.size.height, geometry.size.width)/min(geometry.size.height, geometry.size.width))
                    opacityFraction = 0.0
                    DispatchQueue.main.asyncAfter(deadline: .now() + timeInterval) {
                        scale = 1.0
                        opacityFraction = 0.0
                    }
                }
            })
            .clipped()
        }
    }
}

extension View {
    func rippleEffect(rippleColor: Color = .accentColor.opacity(0.5)) -> some View {
        modifier(Ripple(rippleColor: rippleColor))
    }
}
ee7vknir

ee7vknir2#

我用上面弗雷德里克的答案稍微修改了一下,以达到我想要的结果。

import SwiftUI

// MARK: - ContentView

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
        .rippleEffect(rippleColor: .gray)
        .frame(width: 400, height: 200)
        .padding()
    }
}

// MARK: - Ripple

struct Ripple: ViewModifier {
    // MARK: Lifecycle

    init(rippleColor: Color) {
        color = rippleColor
    }

    // MARK: Internal

    let color: Color

    let timeInterval: TimeInterval = 0.5

    func body(content: Content) -> some View {
        GeometryReader { geometry in
            ZStack {
                Rectangle()
                    .foregroundColor(.gray.opacity(0.05))
                Circle()
                    .foregroundColor(color)
                    .opacity(0.2 * opacityFraction)
                    .scaleEffect(scale)
                    .offset(x: x, y: y)
                content
            }
            .gesture(
                DragGesture(minimumDistance: 0.0)
                    .onChanged { gesture in
                        let location = gesture.startLocation

                        x = location.x - geometry.size.width / 2
                        y = location.y - geometry.size.height / 2

                        opacityFraction = 1.0

                        withAnimation(.linear(duration: timeInterval / 2.0)) {
                            scale = 3.0 *
                                (
                                    max(geometry.size.height, geometry.size.width) /
                                        min(geometry.size.height, geometry.size.width)
                                )
                        }
                    }
                    .onEnded { _ in
                        withAnimation(.linear(duration: timeInterval / 2.0)) {
                            opacityFraction = 0.0
                            scale = 1.0
                        }
                    }
            )
            .clipped()
        }
    }

    // MARK: Private

    @State private var scale: CGFloat = 0.5

    @State private var animationPosition: CGFloat = 0.0
    @State private var x: CGFloat = 0.0
    @State private var y: CGFloat = 0.0

    @State private var opacityFraction: CGFloat = 0.0
}

extension View {
    func rippleEffect(rippleColor: Color = .accentColor.opacity(0.5)) -> some View {
        modifier(Ripple(rippleColor: rippleColor))
    }
}

// MARK: - ContentView_Previews

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

感谢弗雷德里克,为正确的方向推动。

相关问题