Recently, I used React-DND, a drag-and-drop library based on HTML5. The drag-and-drop capability enriches the interaction mode of the front end, and expands various drag-and-drop feedback effects based on the drag-and-drop capability. Therefore, it is necessary to learn about it.

Drag-and-drop interactions found in a variety of front editor, and “editor” is an integrated front comprehensive engineering technical ability, which will involve various forms of drag and drop interactions, because “drag” is one of the important interaction, enhance the user experience so I need to do a variety of customized drag-and-drop interaction effect, as the developers are supposed to master the application of “drag”!

Recently, I am developing a low code platform, so I take this opportunity to share the basic knowledge and practical experience about “drag and drop” interaction, hoping to provide some reference for students who need it.

Drag and drop in HTML5

Drag and Drop are components of HTML5 standard. After understanding and mastering them, it will help improve our ability to design technical solutions in Drag scenarios.

1.1 draggable attribute

In modern browsers, it is not hard to find that image tags () can be long pressed and dragged, but custom DOM nodes can be dragged if required and need to be configured to tell the browser that elements (Element/Tag) support drag and drop.

Whether elements are allowed to be dragged and dropped and respond to API operations depends on the draggable global tag attribute

Draggable is a tag attribute of type Boolean:

  • true: Elements can be dragged
  • false: Elements cannot be dragged

When the element is set to the draggable property, long press to drag it freely:

1.2 Drag & Drop events

HTML drag & Drop uses “DOM events” and “Drag events” inherited from “Mouse events”.

A typical drag-and-drop operation: the user selects a draggable element, drags it over a droppable element, and then releases the mouse.

During a drag element, several drag-and-drop related events are triggered. Events of the drag and dragover types are triggered frequently.

In addition to defining drag event types, each event type is assigned a corresponding event handler

The event type Event handler trigger The binding element
dragstart ondragstart When you start dragging an element Drag and drop
drag ondrag Fires at a certain frequency while the element is being dragged Drag and drop
dragend ondragend When the dragged element is released (🖱️ release, press ESC) Drag and drop
dragenter ondragenter When dragging an element to a releasable target element Place the
dragexit ondragexit When an element becomes no longer the selected target of a drag operation Place the
dragleave ondragleave When dragging an element away from a releasable target element Place the
dragover ondragover When an element is dragged onto a releasable target element (100 ms/ time) Place the
drop ondrop When a drag element is released on a releasable target element Place the

The timing of each event can be simply represented by the following graph:

⚠️ Note: The default behavior of the dragOver event is: “Reset the current drag operation to “none” “. That is, if the dragOver event of the drop element is not prevented, the drop element will not respond to the drop action of the drag element

// Make the element bound to the event support placement
function handleDragOver(e) {
  // Prevents the default reset behavior
  // can be a drop area for drag elements
  e.preventDefault();
}
Copy the code

In terms of design event criteria, if we need to implement drag and drop on our own, we need to think about design in terms of these key events.

1.3 DataTransfer

In the above event types, it is not difficult to find that the drop element and the drag element are bound to their own events, how can drag element and the drop element establish a connection and transfer data?

This involves the DataTransfer object:

The DataTransfer object is used to hold data during drag and drop. It can hold one or more data items, which can be of one or more data types. – DataTransfer – MDN

DataTransfer objects may have different apis on different browsers because of different standards, but there are a few “standard (common)” properties and methods to be familiar with

An example of DataTransfer on Chrome is as follows:

(1) properties

attribute instructions
dropEffect Gets the currently selected drag-and-drop operation type or set to a new type. The values are none, copy, link, move
effectAllowed Provides all available operation types. The values are none, copy, copyLink, copyMove, link, linkMove, move, all, or uninitialized
files Contains a list of all local files available for data transfer. If the drag operation does not involve dragging a file, this property is an empty list
items(read-only) Provide a DataTransferItemList object that contains a list of all dragged data
types(read-only) Provides a strings array of the format set in the Dragstart event.

(2) method

attribute instructions
setData(format, value) Sets the data of the given type. If data for that type does not exist, it is added to the end so that the last item in the type list will be the new format. If data of that type already exists, the existing data is replaced in the same place.
getData(format) Retrieves data of a given type and returns an empty string if that type of data does not exist or data transfer does not contain data
clearData([format]) Deletes the data associated with the given type. Type parameters are optional. If the type is empty or unspecified, the data associated with all types is deleted. If the specified type of data does not exist, or data transfer does not contain any data, this method will have no effect.
`setDragImage(img element, xOffset, yOffset)`

In a simple drag-and-drop scenario, memory can be compared to the setItem() and getItem() methods of the window.localstorage object.

GetData (), however, finds that it can only get values from ondrop events:

1.4 A case study of mastering the drag and drop API

<div>
  <div class="drag" draggable="true" id="dragger" ondragstart="handleDragStart(event)">Drag the elements</div>
  <div class="drop" ondrop="handleDrop(event)" ondragover="allowDrop(event)">Place area</div>
</div>

<script>
  function handleDragStart(e) {
    e.dataTransfer.setData('DRAG_NODE_ID', e.target.id)
  }
  function handleDragOver(e) {
    e.preventDefault();
  }
  function handleDrop(e) {
    e.preventDefault();
    var data = e.dataTransfer.getData('DRAG_NODE_ID');
    e.target.appendChild(document.getElementById(data));
  }
</script>
Copy the code

IO /DYBOY/pen/e…

Effect:

1.6 compatibility

It is the capability proposed by THE HTML5 standard, so there are differences in the support of the standard by major browser manufacturers. The compatibility of the standard is as follows:

It’s much simpler than traditional mousedown, Mousemove, and Mouseup drag-and-drop, with less judgment about where to put the target and less real-time access to the location.

In addition the API is not much, for example, we want to customize drag the image size, style, etc., for the moment, the more convenient solution was found, but from another point of view, let us to drag the ability of design and standard has a more profound understanding, to design and implement drag-and-drop interactions have a “theory” foundation!

Two, hand rub one

With the basics above, implementing a list drag-and-drop sort shouldn’t be too difficult.

2.1 Design and Implementation

In combination with the above Drag & Drop event types, Drag sorting is mainly a logical sorting of the interaction between Drag objects. Here we temporarily classify it as:

  • Source object: A single list item dragged in a drag list
  • Target object: Drag and drop list items that interact with source objects

The overall design idea of interactive events is as follows:

(1) ondragstart

At this point, the time to start dragging “source object”, in this event callback function to change the “source object” style, set some of the drag pass parameters and other initial values.

// The source object starts dragging
const handleDragStart = (e: React.DragEvent<HTMLDivElement>) = > {
  e.dataTransfer.effectAllowed = "move";
  setDragId(e.currentTarget.dataset.index); // Get the id of the drag item from the dataset
};
Copy the code

(2) ondragover

The target object is interacting with the “source object” in the drag. At this time, the “source object” is directly above the “target object”. The target object calls the callback event declared in the “target object” ondragover at the frequency of 100ms/ time.

At this point, we calculate and change the position of the source object and target object.

// When the source object is above the target object
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) = > {
  e.preventDefault(); // Allow placement, prevent default events
  const dropId = e.currentTarget.dataset.index;
  move(dragId, dropId); // Change the original list data
};
Copy the code

(3) ondrag

This event applies to the “source object”, which is in the drag process. At this time, the style properties of opacity, display(None), and Visiblity of the source object can be changed. If the dragstart event changes, the drag copy object will be lost.

// The source object is being dragged
const handleDrag = (e: React.DragEvent<HTMLDivElement>) = > {
  e.currentTarget.style.opacity = "0";
};
Copy the code

(4) ondragend

When the source object is released, the event bound to the source object is actively called, at which point the changed style is restored.

// When the source object is placed
const handleDragEnd = (e: React.DragEvent<HTMLDivElement>) = > {
  e.currentTarget.style.opacity = "1";
};
Copy the code

2.2 Implementation Effect

2.3 Point animation

The effect in the above implementation is ok, but without the animation of the switching process of drag items, the sorting of the original list data is directly modified by move(dragId, dropId) method in the dragover event, resulting in switching mutation.

Add CSS frame animation with animation:

@keyframes dropUp {
  100% {
    transform: translateY(5px); }}@keyframes dropDown {
  100% {
    transform: translateY(-5px); }}.drop-up{
  animation: dropUp 0.3 s ease-in-out forwards;
}
.drop-down{
  animation: dropDown 0.3 s ease-in-out forwards;
}
Copy the code

The same logic is added to the dragOver event:

// When the source object is above the target object
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) = >{...// Set the animation
  const dropId = e.currentTarget.dataset.index;
  const dragIndex = findIndex(listData, (i) = > i.id === dragId);
  const dropIndex = findIndex(listData, (i) = > i.id === dropId);
  // Add the corresponding CSS class to achieve the visual animation transition
  e.currentTarget.classList.remove("drop-up"."drop-down");
  if (dragIndex < dropIndex) {
    e.currentTarget.classList.add("drop-down");
  } else if (dragIndex > dropIndex) {
    e.currentTarget.classList.add("drop-up"); }... };Copy the code

Added animation effects:

Looks a little better, of course, you can go to expand the effects of animation, or use the tripartite animation library.

Three, the existing drag library

At present, the mainstream drag library includes:

  • react-dnd: Github.com/react-dnd/r…
  • react-beautiful-dnd: Github.com/atlassian/r…
  • sortablejs: sortablejs.github.io/Sortable/
  • react-sortable-hoc: Github.com/clauderic/r…

For more on the differences, see: Review of the Drag-and-drop plugin used in React.

Four,

In view of extensibility and compatibility, react-DND was selected as the basic drag library for low-code platforms. Of course, in complex drag scenarios, you need to expand the drag library by yourself, which is relatively difficult to get started. However, with the “drag knowledge” as the pre-foundation, It’s not that hard to extend functionality.

Friends can follow the wechat public account DYBOY and come and play together