使用拖动手势中断SwiftUI动画

4ioopgfo  于 2023-08-02  发布在  Swift
关注(0)|答案(1)|浏览(108)

苹果今年已经使其easier than ever在执行拖动手势后平滑地动画视图,通过自动使用拖动手势的最终速度作为动画的初始速度。下面是一个简单的例子:

struct DraggableCircle: View {
    @State private var offset: CGSize = .zero
    
    var body: some View {
        let gesture = DragGesture(minimumDistance: 0)
            .onChanged { gesture in
                offset = gesture.translation
            }
            .onEnded { _ in
                withAnimation(.bouncy(duration: 2)) {
                    offset = .zero
                }
            }
        
        Circle()
            .frame(width: 200, height: 200)
            .offset(offset)
            .gesture(gesture)
    }
}

字符串


的数据
但是,这段代码缺少的是在动画结束之前通过启动一个新的拖动手势来平滑地中断圆的动画的能力。相反,圆圈将立即完成动画,从你的手指上跳开:



这是完全有意义的,因为当手势结束时,我们将offset设置回.zero,而没有做任何事情来确定动画中断时圆圈在屏幕上的位置。

什么是最直接的方法,使这项工作没有任何突然的跳跃?

一种可能的方法是使用手势的位置而不是其平移来定位圆圈,这总是将圆圈放置在手势发生的位置:

struct DraggableCircle_v2: View {
    @State private var position: CGPoint?
    
    var body: some View {
        let gesture = DragGesture(minimumDistance: 0)
            .onChanged { gesture in
                position = gesture.location
            }
            .onEnded { _ in
                withAnimation(.bouncy(duration: 2)) {
                    position = nil
                }
            }
        
        GeometryReader { geo in
            let center = CGPoint(
                x: geo.size.width / 2,
                y: geo.size.height / 2
            )
            
            Circle()
                .frame(width: 200, height: 200)
                .position(position ?? center)
                .gesture(gesture)
        }
    }
}


这将适用于大多数用例。然而,这总是将圆圈的“中心”放在手指所在的位置,而不考虑手势在圆圈上的开始位置。换句话说,我们没有朝着找出动画中断时圆的位置的目标取得任何进展。


我们是否可以使用手势中断动画,并找出中断时圆的位置?
编辑:以下是我尝试实现的目标:


mjqavswn

mjqavswn1#

让我们使用一个单独的变量来跟踪拖动时圆的位置(dragOffset),并使用一个单独的变量来将圆动画返回到其初始位置(animationOffset)。我们将分别使用这两个变量来实现平滑行为。

struct ContentViewC: View {
    var body: some View {
        ZStack {
            Color.white.edgesIgnoringSafeArea(.all)
            DraggableCircleView()
        }
    }
}

struct DraggableCircleView: View {
@GestureState private var dragOffset: CGSize = .zero
@State private var animationOffset: CGSize = .zero

var body: some View {
    VStack {
        Circle()
            .frame(width: 200, height: 200)
            .foregroundColor(.blue)
            .offset(animationOffset) // Use animationOffset for smooth animation
            .offset(dragOffset) // Use dragOffset for smooth dragging
            .gesture(DragGesture()
                .updating($dragOffset, body: { (value, dragOffset, _) in
                    dragOffset = value.translation
                })
                .onEnded { value in
                    // Save the final drag position in animationOffset
                    animationOffset = value.translation
                    withAnimation(.easeOut(duration: 1.5)) {
                        // Animate the circle back to its initial position
                        animationOffset = .zero
                    }
                }
            )
    }
}}

字符串
在这段代码中,我们使用dragOffset在拖动过程中更新圆的位置,并在拖动结束时使用animationOffset进行平滑动画。此分离应防止反弹效果,并在将圆拖回到中心并设置动画时给予更平滑的体验。
当视图层次结构中有两个.offset修饰符时,SwiftUI会将它们组合起来,并将总偏移量应用于视图。
您可以使用以下命令交换2x偏移以提高可读性:

.offset(CGSize(width: animationOffset.width + dragOffset.width,
                                         height: animationOffset.height + dragOffset.height))


但是2倍偏移看起来更干净。

相关问题