Quiero hacer un componente React que se pueda arrastrar (es decir, reposicionable con el mouse), que parece involucrar necesariamente el estado global y los controladores de eventos dispersos. Puedo hacerlo de la manera sucia, con una variable global en mi archivo JS, y probablemente podría incluso envolverlo en una interfaz de cierre agradable, pero quiero saber si hay una forma que se adapte mejor a React.
Además, dado que nunca antes había hecho esto en JavaScript sin formato, me gustaría ver cómo lo hace un experto, para asegurarme de que tengo todos los casos de esquina manejados, especialmente en lo que se relaciona con React.
Gracias.
javascript
reactjs
Andrew Fleenor
fuente
fuente
Respuestas:
Probablemente debería convertir esto en una publicación de blog, pero aquí hay un ejemplo bastante sólido.
Los comentarios deberían explicar las cosas bastante bien, pero avíseme si tiene preguntas.
Y aquí está el violín para jugar: http://jsfiddle.net/Af9Jt/2/
Pensamientos sobre la propiedad estatal, etc.
"Quién debe ser dueño de qué estado" es una pregunta importante a responder, desde el principio. En el caso de un componente "arrastrable", pude ver algunos escenarios diferentes.
escenario 1
el padre debe poseer la posición actual del arrastrable. En este caso, el arrastrable aún poseería el estado "estoy arrastrando", pero llamaría
this.props.onChange(x, y)
cada vez que ocurra un evento mousemove.Escenario 2
el padre solo necesita poseer la "posición sin movimiento", por lo que el arrastrable poseería su "posición de arrastre", pero en el mouse llamaría
this.props.onChange(x, y)
y pospondría la decisión final al padre. Si al padre no le gusta dónde terminó el arrastrable, simplemente no actualizará su estado, y el arrastrable volverá a su posición inicial antes de arrastrarlo.¿Mixin o componente?
@ssorallen señaló que, debido a que "arrastrable" es más un atributo que una cosa en sí misma, podría servir mejor como mezcla. Mi experiencia con mixins es limitada, por lo que no he visto cómo podrían ayudar o interponerse en situaciones complicadas. Esta bien podría ser la mejor opción.
fuente
Mixin
que una clase completa, ya que "Arrastrable" no es en realidad un objeto, es una capacidad de un objeto.var computedStyle = window.getComputedStyle(this.getDOMNode()); pos = { top: parseInt(computedStyle.top), left: parseInt(computedStyle.left) };
Si está usando jquery con react, probablemente esté haciendo algo mal;) Si necesita algún complemento de jquery, encuentro que generalmente es más fácil y menos código reescribirlo en puro react.this.getDOMNode().getBoundingClientRect()
: getComputedStyle puede generar cualquier propiedad CSS válida, incluidoauto
en cuyo caso el código anterior dará como resultado unNaN
. Consulte el artículo de MDN: developer.mozilla.org/en-US/docs/Web/API/Element/…this.getDOMNode()
, eso ha sido obsoleto. Utilice una referencia para obtener el nodo dom. facebook.github.io/react/docs/…Implementé react-dnd , un mixin flexible de arrastrar y soltar HTML5 para React con control DOM completo.
Las bibliotecas de arrastrar y soltar existentes no se ajustaban a mi caso de uso, así que escribí la mía propia. Es similar al código que hemos estado ejecutando durante aproximadamente un año en Stampsy.com, pero reescrito para aprovechar React y Flux.
Requisitos clave que tenía:
Si te suenan familiares, sigue leyendo.
Uso
Fuente de arrastre simple
Primero, declare los tipos de datos que se pueden arrastrar.
Se utilizan para comprobar la "compatibilidad" de las fuentes de arrastre y los destinos de colocación:
(Si no tiene varios tipos de datos, es posible que esta biblioteca no sea para usted).
Luego, hagamos un componente arrastrable muy simple que, cuando se arrastra, representa
IMAGE
:Al especificar
configureDragDrop
, contamosDragDropMixin
el comportamiento de arrastrar y soltar de este componente. Tanto los componentes que se pueden arrastrar como los que se pueden soltar utilizan la misma mezcla.En el interior
configureDragDrop
, necesitamos llamarregisterType
para cada uno de nuestrosItemTypes
componentes personalizados que admite. Por ejemplo, puede haber varias representaciones de imágenes en su aplicación, y cada proporcionaría unadragSource
deItemTypes.IMAGE
.A
dragSource
es solo un objeto que especifica cómo funciona la fuente de arrastre. Debe implementarbeginDrag
para devolver el elemento que representa los datos que está arrastrando y, opcionalmente, algunas opciones que ajustan la IU de arrastre. Opcionalmente, puede implementarcanDrag
para prohibir el arrastre, oendDrag(didDrop)
ejecutar alguna lógica cuando la caída ha ocurrido (o no). Y puede compartir esta lógica entre componentes dejando que se genere un mixin compartidodragSource
para ellos.Finalmente, debe usar
{...this.dragSourceFor(itemType)}
en algunos (uno o más) elementosrender
para adjuntar controladores de arrastre. Esto significa que puede tener varios "controles de arrastre" en un elemento, e incluso pueden corresponder a diferentes tipos de elementos. (Si no está familiarizado con los atributos de difusión de JSX sintaxis de , compruébelo).Destino de caída simple
Digamos que queremos
ImageBlock
ser un objetivo de caída paraIMAGE
s. Es más o menos lo mismo, excepto que necesitamos darregisterType
unadropTarget
implementación:Arrastrar origen + soltar destino en un componente
Supongamos que ahora queremos que el usuario pueda arrastrar una imagen fuera de
ImageBlock
. Solo necesitamos agregarle lo apropiadodragSource
y algunos controladores:¿Qué más es posible?
No he cubierto todo, pero es posible usar esta API de varias maneras más:
getDragState(type)
ygetDropState(type)
para saber si el arrastre está activo y úselo para alternar clases o atributos de CSS;dragPreview
deseaImage
usar imágenes como marcadores de posición de arrastre (useImagePreloaderMixin
para cargarlas);ImageBlocks
reordenables. Solo los necesitamos para implementardropTarget
ydragSource
paraItemTypes.BLOCK
.dropTargetFor(...types)
permite especificar varios tipos a la vez, por lo que una zona de caída puede capturar muchos tipos diferentes.Para obtener documentación e instrucciones de instalación actualizadas, diríjase a react-dnd repo en Github .
fuente
La respuesta de Jared Forsyth es horriblemente incorrecta y anticuada. Sigue un conjunto completo de antipatrones, como el uso de
stopPropagation
, el estado de inicialización de los accesorios , el uso de jQuery, los objetos anidados en el estado y tiene algunosdragging
campo de estado . Si se reescribe, la solución será la siguiente, pero todavía fuerza la reconciliación del DOM virtual en cada tic de movimiento del mouse y no es muy eficaz.UPD. Mi respuesta fue horriblemente incorrecta y anticuada. Ahora, el código alivia los problemas del ciclo de vida lento de los componentes de React mediante el uso de controladores de eventos nativos y actualizaciones de estilo, utiliza
transform
ya que no conduce a reflujos y acelera los cambios de DOMrequestAnimationFrame
. Ahora es constantemente 60 FPS para mí en cada navegador que probé.y un poco de CSS
Ejemplo en JSFiddle.
fuente
He actualizado la solución polkovnikov.ph a React 16 / ES6 con mejoras como el manejo táctil y el ajuste a una cuadrícula, que es lo que necesito para un juego. Ajustar a una cuadrícula alivia los problemas de rendimiento.
fuente
react-draggable también es fácil de usar. Github:
https://github.com/mzabriskie/react-draggable
Y mi index.html:
Necesita sus estilos, que es corto, o no obtiene el comportamiento esperado. Me gusta el comportamiento más que algunas de las otras opciones posibles, pero también hay algo llamado reaccionar-redimensionable-y-movible . Estoy tratando de hacer que el cambio de tamaño funcione con arrastrable, pero no me gusta hasta ahora.
fuente
Aquí está un simple enfoque moderno de esto con
useState
,useEffect
yuseRef
en la ES6.fuente
Me gustaría agregar un tercer escenario
La posición de movimiento no se guarda de ninguna manera. Piense en ello como un movimiento del mouse: su cursor no es un componente de React, ¿verdad?
Todo lo que hace es agregar un accesorio como "arrastrable" a su componente y una secuencia de eventos de arrastre que manipularán el dom.
En este caso, una manipulación DOM es algo elegante (nunca pensé que diría esto)
fuente
setXandY
función con un componente imaginario?Actualicé la clase usando referencias, ya que todas las soluciones que veo aquí tienen cosas que ya no son compatibles o que pronto se depreciarán como
ReactDOM.findDOMNode
. Puede ser padre de un componente hijo o un grupo de hijos :)fuente
Aquí hay una respuesta de 2020 con un gancho:
Luego, un componente que usa el gancho:
fuente