Issue #898

Change element position using either offset or position, and use DragGesture

Use GestureState to store the updating startDragLocation to keep the start location when drag begins, so we can add translation

struct MoveModifier: ViewModifier {
    @Binding var position: CGPoint

    @GestureState private var startLocation: CGPoint?
    
    func body(content: Content) -> some View {
        content
            .gesture(gesture)
    }
    
    private var gesture: some Gesture {
        DragGesture()
            .onChanged { value in
                var position = startLocation ?? position
                position.x += value.translation.width
                position.y += value.translation.height
                self.position = position
            }
            .updating($startLocation) { (value, gestureState, transaction) in
                gestureState = gestureState ?? position
                transaction.animation = .easeInOut
            }
            .onEnded { _ in
                // No op
            }
    }
}

struct CanvasView: View {
    @GestureState private var startDragLocation: CGPoint?

    private var elementsView: some View {
        ForEach($viewModel.elements) { $element in
            ElementView(
                element: $element
            )
            .offset(x: element.position.x, y: element.position.y)
            // .position(element.position)
            .onTapGesture {
                // on tap
            }
            .modifier(MoveModifier(position: $element.position))
        }
    }
}

Read more