LeDoc
LeDoc

Reputation: 945

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)

Upvotes: 9

Views: 16313

Answers (5)

riv
riv

Reputation: 7324

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

Upvotes: 10

Simon
Simon

Reputation: 275

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

Upvotes: 5

Hojiakbar Karimov
Hojiakbar Karimov

Reputation: 101

As far as I understand all you need offset of currently dragging element, via HTML5Backend you can't get, but if you use MouseBackend you can easily get the coordinates https://github.com/zyzo/react-dnd-mouse-backend

see example of with preview

Upvotes: 0

Jeton Tha&#231;i
Jeton Tha&#231;i

Reputation: 815

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.

Upvotes: 4

Mos&#232; Raguzzini
Mos&#232; Raguzzini

Reputation: 15821

You can use

ReactDOM.findDOMNode(component).getBoundingClientRect();

Ref: https://reactjs.org/docs/react-dom.html#finddomnode

Or just make a ref to your component instance and get getBoundingClientRect() on the instance within a onmousemove event.

Upvotes: 3

Related Questions