Arrastrar y soltar nativo en combate y análisis HTML5 --- realización de componentes de arrastrar y soltar en React

Prefacio: Los componentes de arrastrar y soltar son una característica muy común en el desarrollo front-end Ahora, ya sea que use React o Vue, hay muchos componentes de arrastrar y soltar listos para usar que se pueden usar. Sin embargo, a veces es posible que aún necesite implementarlo usted mismo, por lo que debe comprender sus principios de implementación.

Antecedentes: asumí una tarea esta semana para hacer que el cuadro de lanzadera de antD se pueda arrastrar (se pueden arrastrar tanto hacia la izquierda como hacia la derecha, y se admite la función de arrastrar y soltar de selección múltiple). Después de mirar la api de antD, descubrí que no hay configuración, así que decidí escribir una implementación nativa yo mismo. Tomé prestada la demostración del tipo grande, vi cómo se implementa arrastrar y soltar, y luego la apliqué en el proyecto.

Comprender arrastrar y soltar en HTML

Hoy en día, la mayoría de los componentes frontales de arrastrar y soltar se basan en la interfaz de arrastrar y soltar proporcionada de forma nativa por HTML5. Entonces, antes de comenzar a encapsular componentes con un marco específico , debe descubrir estas funciones de interfaz nativa.

El evento de arrastre se agrega al evento del mouse DOM de HTML 5. Para un elemento de página con el conjunto de atributos que se pueden arrastrar , siempre que se arrastre a un elemento con el mismo atributo que se puede soltar, se completa una función completa de arrastrar y soltar. Durante este proceso, los siguientes tipos de eventos se activarán respectivamente: 

Sugerencia: Los enlaces y las imágenes se pueden arrastrar de forma predeterminada, no se requiere ningún atributo arrastrable.

arrastrable tiene tres valores de la siguiente manera:

draggable = true (el elemento se puede arrastrar)

draggable = false (el elemento no se puede arrastrar)

draggable = auto (el navegador puede decidir independientemente si se puede arrastrar un elemento)

Eventos desencadenados por el elemento arrastrado (elemento fuente):

* ondragstart: se activa cuando un elemento comienza a arrastrarse (el evento dragstart se activa cuando se presiona el botón del mouse y el mouse comienza a moverse) * ondrag: se activa
cuando se arrastra el elemento (el evento de arrastre continuará activándose mientras el elemento se está arrastrando, similar a los eventos mousemove y touchmove)
* ondragend: se activa después de que el usuario termina de arrastrar el elemento, cuando el arrastre se detiene (ya sea que el elemento se coloque en un destino de colocación válido o en un destino de colocación no válido), el evento dragend ocurrira.

Nota: De forma predeterminada, el navegador no cambiará la apariencia del elemento arrastrado durante el arrastre. Pero puedes modificarlo tú mismo. Sin embargo, la mayoría de los navegadores crean una copia translúcida del elemento que se arrastra, que siempre sigue al cursor. Cuando se arrastra un elemento a un destino de colocación válido, se activan los siguientes eventos:

Evento desencadenado cuando se suelta el elemento arrastrado (elemento de destino) :

* ondraenter: activa este evento cuando el objeto arrastrado por el mouse ingresa al rango de su contenedor (similar al evento mouseover) *
ondragover: se activa cuando el objeto arrastrado por el mouse pasa por un elemento (el evento dragover se activará continuamente, cada 100 Se activa una vez en milisegundos, similar al evento mousemove)
* ondragleave: se activa cuando el objeto arrastrado por el mouse sale de su rango de contenedor (similar al evento mouseout)
* ondrop: se activa en el elemento soltado cuando se suelta la operación de arrastre

Es necesario prestar atención: en eventos dragenter, dragover (dragend), debemos evitar el comportamiento predeterminado del navegador, para que los elementos que arrastramos se conviertan en elementos liberables.

Después de familiarizarse con estos tipos de eventos básicos, la implementación consiste en vincular las funciones de procesamiento de eventos correspondientes en el objeto de origen y el objeto de destino respectivamente, y escuchar el procesamiento.

Además de estas interfaces de eventos de arrastrar y soltar, generalmente también necesitamos manejar la transferencia de datos. HTML5 también proporciona una interfaz simple. En la función de escucha correspondiente, podemos obtener el objeto de evento. Hay una interfaz de transferencia de datos dentro de este objeto, que puede usarse especialmente para guardar el contenido de datos del evento. Los métodos correspondientes a DataTransfer son:

Procesamiento de datos llevado a cabo por arrastrar y soltar

* event.dataTransfer.setData (formato, datos) Agregue datos de arrastrar y soltar, este método recibe dos parámetros, el primer parámetro es el tipo de datos (se puede personalizar, solo complete "texto/simple" similar o "textml" Texto que representa el tipo MIME), el segundo parámetro son los datos a transportar;

* event.dataTransfer.getData(formato) Operación inversa, obtener datos, solo acepta un parámetro, a saber, tipo de datos;

* event.dataTransfer.clearData(format) Borrar datos; eliminar los datos en el formato especificado del objeto dataTransfer, el parámetro es opcional, si no se proporciona ningún parámetro, se eliminarán todos los datos del objeto.

* event.dataTransfer.setDragImage(el, x, y) puede personalizar la imagen al lado del mouse durante arrastrar y soltar; establecer el ícono de la operación de arrastrar y soltar, donde el representa el ícono personalizado y x representa la distancia entre el icono y el ratón en dirección horizontal, y representa la distancia vertical entre el icono y el ratón

La siguiente es una demostración simple ~

import React, { useState, useRef } from 'react';

const list = [
  {
    uid: '1',
    text: '序列1',
  },
  {
    uid: '2',
    text: '序列2',
  },
  {
    uid: '3',
    text: '序列3',
  },
  {
    uid: '4',
    text: '序列4',
  },
  {
    uid: '5',
    text: '序列5',
  },
];

const Drag: React.FC<{}> = () => {
  // console.log('list', list)
  const [rightList, setRightList] = useState(list);
  const [leftList, setLeftList] = useState([]);

  //鼠标华划过接受拖拽元素的事件
  const handleDrop = (callBack, e, arrow) => {
    e.preventDefault();  //阻止默认事件:防止打开拖拽元素的url(Firefox)
    console.log('handleDrop', callBack, e, arrow);
    const {
      dataset: { id },
    } = e.target;
    console.log('id', id);

    const curData = JSON.parse(e.dataTransfer.getData('itemData'));
    console.log('curData', curData);
    callBack((prevData) => {
      console.log('prevData', prevData);
      const diffData = prevData.filter((item) => item.uid !== curData.uid);
      // id 不存在是在不同盒子内拖拽  存在则是在本身盒子内拖拽
      // 项目中发现, 只要鼠标经过元素, 且元素被自定义过data-id属性, id都可以都能拿到!
      if (!id) return [...diffData, curData];
      // 找到鼠标划过的目标元素的其盒子内的位置
      const index = diffData.findIndex((item) => item.uid === id);
      //把拖拽元素放置在鼠标划过元素的上方
      diffData.splice(index, 0, curData);
      return diffData;
    });
    //朝左拖拽
    if (arrow === 'left') {
      setRightList((prvData) => prvData.filter((item) => item.uid !== curData.uid));
    }
    // 朝右拖拽
    else {
      setLeftList((prvData) => prvData.filter((item) => item.uid !== curData.uid));
    }
  };
  // 拖拽元素进入目标元素时触发事件-为目标元素添加拖拽元素进入时的样式效果
  const handleDragEnter = (e) => e.target.classList.add('over');

  // 拖拽元素离开目标元素时触发事件-移除目标元素的样式效果
  const handleDragLeave = (e) => e.target.classList.remove('over');

  return (
    <div>
      <div
        style={
   
   { width: '300px', height: '300px' }}
        onDragOver={(e) => {
          e.preventDefault();
        }}
        onDrop={(e) => handleDrop(setLeftList, e, 'left')}
      >
        {leftList.map((item) => (
          <div
            className="item"
            draggable
            key={item.uid}
            data-id={item.uid}
            onDragStart={(e) => {
              e.dataTransfer.setData('itemData', JSON.stringify(item));
            }}
          >
            {item.text}
          </div>
        ))}
      </div>
      <div
        style={
   
   { width: '300px', height: '300px' }}
        onDragOver={(e) => {
          e.preventDefault();
        }}
        onDrop={(e) => handleDrop(setRightList, e, 'right')}
        onDragEnter={handleDragEnter}
        onDragLeave={handleDragLeave}
      >
        {rightList.map((item) => (
          <div
            className="item"
            draggable
            key={item.uid}
            data-id={item.uid}
            onDragStart={(e) => {
              e.dataTransfer.setData('itemData', JSON.stringify(item));
            }}
          >
            {item.text}
          </div>
        ))}
      </div>
    </div>
  );
};

export default Drag;

Procesamiento de estilo adicional (consulte cuando se requiera procesamiento de estilo personalizado)

Agregar efectos de arrastrar y soltar

Para lograr el efecto visual de arrastrar y soltar, las dos propiedades effectAllowed y dropEffect deben usarse en combinación.

dropEffect: establezca el comportamiento de arrastrar y soltar permitido por el destino de arrastrar y soltar. Si el comportamiento de arrastrar y soltar establecido aquí no está dentro del comportamiento de arrastrar y soltar establecido por la propiedad effectAllowed, el -La operación de soltar fallará. El valor del atributo solo puede ser "nulo", "copiar", "enlazar" o "mover";

effectAllowed: establece el comportamiento de arrastre permitido por el elemento de arrastre. El valor del atributo puede ser "ninguno", "copiar", "copiarEnlace", "copiarMove", "enlace", "enlaceMove", "mover", "todos" o " no inicializado";

// 先设置一些效果常量:(内置属性) 
// 可以通过props传递

export const All = "all";

export const Move = "move";

export const Copy = "copy";

export const Link = "link";

export const CopyOrMove = "copyMove";

export const CopyOrLink = "copyLink";

export const LinkOrMove = "linkMove";

export const None = "none";


// 被拖拽组件
const Drag = (props) => {

    // 如果想自定义拖拽图像(非必须)
    const [isDragging, setIsDragging] = React.useState(false);
    const image = React.useRef(null);
    React.useEffect(() => {
        image.current = null;
        if (props.dragImage) {
            image.current = new Image();
            image.current.src = props.dragImage;
        }

    }, [props.dragImage]);

    // 拖拽开始
    const startDrag = e => {
        setIsDragging(true);
        // 设置数据(必须)
        e.dataTransfer.setData("drag-item", props.dataItem);
        // 设置effectAllowed属性添加效果(非必须)
        e.dataTransfer.effectAllowed = props.dropEffect;
        // 设置图片(非必须)
        if (image.current) {
            e.dataTransfer.setDragImage(image.current, 0, 0);
        }
    };

    // 拖拽结束
    const dragEnd = () => setIsDragging(false);  

    return (
        {props.children}
    );
}


// 目标组件
const DropTarget = (props) => {

    // 滑过时
    const dragOver = e => {
        // 阻止默认行为
        e.preventDefault();
        // 添加效果(非必须)
        e.dataTransfer.dropEffect = props.dropEffect;
    }

    // 进入时(非必须)
    const dragEnter = e => {
        e.dataTransfer.dropEffect = props.dropEffect;
    }

    // 释放时
    const drop = e => {
        // 获取数据(必须)
        const droppedItem = e.dataTransfer.getData("drag-item");
        // 触发回调函数(必须)
        if (droppedItem) {
            props.onItemDropped(droppedItem);
        }
    }

    return (
        {props.children}
    )

}

// 样式(可以根据isDragging来进行判断)
const draggingStyle = {
    opacity: 0.25,
};

【Soporte del navegador】

Actualmente, solo Internet Explorer 9, Firefox, Opera 12, Chrome y Safari5 admiten arrastrar y soltar, y Safari5.1.2 no admite arrastrar y soltar.

Una nota sobre destinos de colocación personalizados y compatibilidad con navegadores

  Al arrastrar un elemento más allá de algunos destinos de colocación no válidos, puede ver un gesto especial del mouse (una barra invertida en un círculo), que indica que no se puede colocar. Si bien todos los elementos admiten eventos de destino de colocación, estos elementos no permiten la colocación de forma predeterminada. Si el elemento arrastrado pasa por un elemento que no se puede soltar, no importa lo que haga el usuario, el evento de soltar no ocurrirá. Sin embargo, puede hacer que cualquier elemento sea un destino de colocación válido anulando el comportamiento predeterminado de los eventos dragenter y dragover ( event.preventDefault ).

  Después de anular el comportamiento predeterminado, encontrará que cuando el elemento arrastrado se mueve al destino de colocación, el cursor se convierte en un símbolo de colocación permitida. En Firefox 3.5+, el comportamiento predeterminado para soltar eventos es abrir la URL que se soltó en el destino de soltar. Si se arrastra una imagen al destino de colocación, la página se redirigirá al archivo de imagen. Si el texto se arrastra y se suelta en un destino de colocación, se generará un error de URL no válida. Entonces, para que Firefox admita arrastrar y soltar normalmente, es necesario cancelar el comportamiento predeterminado del evento de soltar y evitar que se abra la URL del elemento arrastrado.

premio:

1. Aprendizaje de eventos nativos de arrastrar y soltar H5 y familiaridad con otros eventos nativos

2. Familiaridad con reaccionar Hooks+ TS

3. Lógica de procesamiento de desactivación de datos y arrastrar y soltar

insuficiente:

1. El estilo de arrastrar y soltar de selección múltiple aún no está implementado

2. Todavía no estoy lo suficientemente familiarizado con los eventos nativos

Referencia: https://blog.csdn.net/weixin_45750771/article/details/125546827

https://blog.csdn.net/weixin_35521120/article/details/113522235

Supongo que te gusta

Origin blog.csdn.net/BUG_CONQUEROR_LI/article/details/126758117
Recomendado
Clasificación