繁体   English   中英

SwiftUI 涟漪效应 Animation

[英]SwiftUI Ripple Effect Animation

我正在努力在 SwiftUI 中创建一种类似于此处的涟漪效应。

这是我到目前为止所拥有的:

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))
    }
}

下一步是进行缩放 animation,但我不知道如何操作。 我试过使用缩放修改器应用缩放效果和过渡,但似乎没有任何效果。

有人可以帮助我实现我正在寻找的连锁反应吗?

此外,如果这样的东西已经存在,我很乐意使用它,但我还没有找到任何东西。

谢谢,

收入客公里

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

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))
    }
}

使用上面 Frederik 的回答,我稍微修改了它以达到我想要的结果。

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()
    }
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM