Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React DND - get coordinates of dragged element as the mouse moves

I have an image that when I drag I want to implement a rotation too. The solution I had in mind was to use React DnD to get the xy coordinates of the dragged image position and calculate the difference between the original image location. The value from this difference would form the basis to make the rotation.

I've looked at examples on the ReactDnD library and see that the DragSource Specification can access the Monitor variable. This monitor variable has access to methods like getInitialClientOffset(). When I implement a console.log() of this value it shows me the last last coordinate set when I release the mouse.

Using this library, is there an easy way to get the current position of the dragged element as I move the mouse?

import React from 'react'
import { DragSource } from 'react-dnd'

// Drag sources and drop targets only interact
// if they have the same string type.
// You want to keep types in a separate file with
// the rest of your app's constants.
const Types = {
  CARD: 'card',
}

/**
 * Specifies the drag source contract.
 * Only `beginDrag` function is required.
 */
const cardSource = {
  beginDrag(props,monitor,component) {
    // Return the data describing the dragged item
    const clientOffset = monitor.getSourceClientOffset();
    const item = { id: props.id }
    console.log(clientOffset);
    return item
  },

  isDragging(props, monitor){
    console.log(monitor.getClientOffset())
  },

  endDrag(props, monitor, component) {
    if (!monitor.didDrop()) {
      return
    }

    // When dropped on a compatible target, do something
    const item = monitor.getItem()
    const dropResult = monitor.getDropResult()
    console.log(item,dropResult)
    //CardActions.moveCardToList(item.id, dropResult.listId)
  },
}

/**
 * Specifies which props to inject into your component.
 */
function collect(connect, monitor) {
  return {
    // Call this function inside render()
    // to let React DnD handle the drag events:
    connectDragSource: connect.dragSource(),
    // You can ask the monitor about the current drag state:
    isDragging: monitor.isDragging(),
  }
}

function Card(props) {
  // Your component receives its own props as usual
  const { id } = props

  // These two props are injected by React DnD,
  // as defined by your `collect` function above:
  const { isDragging, connectDragSource } = props



  return connectDragSource(
    <div>
      I am a draggable card number {id}
      {isDragging && ' (and I am being dragged now)'}
    </div>,
  )
}

// Export the wrapped version
export default DragSource(Types.CARD, cardSource, collect)(Card)
like image 212
LeDoc Avatar asked Apr 16 '19 12:04

LeDoc


3 Answers

The useDrop hook has a hover(item, monitor) method which does what you are looking for. It triggers repeatedly while you are hovering over the drop target.

like image 75
Jeton Thaçi Avatar answered Sep 22 '22 15:09

Jeton Thaçi


I'm posting this way after the fact, but in case anyone is looking for this answer, the react-dnd way of doing this is by using what they call a Drag Layer - it gives you a way to use a custom component to be displayed when dragging.

They have a full example here: https://codesandbox.io/s/github/react-dnd/react-dnd/tree/gh-pages/examples_hooks_js/02-drag-around/custom-drag-layer?from-embed=&file=/src/CustomDragLayer.jsx

Also in the docs you want to look at useDragLayer and DragLayerMonitor

like image 44
Simon Avatar answered Sep 23 '22 15:09

Simon


From what I remember, custom drag layer had serious performance issues. It's better to directly subscribe to the underlying API (the official documentation is severely lacking, but you can get this information from reading drag layer source):

const dragDropManager = useDragDropManager();
const monitor = dragDropManager.getMonitor();

React.useEffect(() => monitor.subscribeToOffsetChange(() => {
  const offset = monitor.getClientOffset();
  // do stuff like setState, though consider directly updating style through refs for performance
}), [monitor]);

You can also use some flag based on drag state to only subscribe when needed (simple isDragging && monitor.subscribe... plus dependencies array does the trick).

like image 24
riv Avatar answered Sep 21 '22 15:09

riv