Je veux créer un composant React déplaçable (c'est-à-dire repositionnable à la souris), qui semble nécessairement impliquer des gestionnaires d'événements globaux et dispersés. Je peux le faire à la manière sale, avec une variable globale dans mon fichier JS, et pourrais probablement même l'envelopper dans une belle interface de fermeture, mais je veux savoir s'il existe un moyen qui se maille mieux avec React.
De plus, comme je n'ai jamais fait cela en JavaScript brut auparavant, j'aimerais voir comment un expert le fait, pour m'assurer que tous les cas secondaires sont traités, en particulier en ce qui concerne React.
Merci.
javascript
reactjs
Andrew Fleenor
la source
la source
Réponses:
Je devrais probablement en faire un article de blog, mais voici un exemple assez solide.
Les commentaires devraient bien expliquer les choses, mais faites-moi savoir si vous avez des questions.
Et voici le violon avec lequel jouer: http://jsfiddle.net/Af9Jt/2/
Réflexions sur la propriété de l'État, etc.
«À qui appartient quel État» est une question importante à laquelle il faut répondre, dès le départ. Dans le cas d'un composant "déplaçable", j'ai pu voir quelques scénarios différents.
Scénario 1
le parent doit posséder la position actuelle du déplaçable. Dans ce cas, le draggable posséderait toujours l'état «suis-je en train de glisser», mais serait appelé
this.props.onChange(x, y)
chaque fois qu'un événement mousemove se produit.Scénario 2
le parent n'a besoin que de posséder la "position non mobile", et ainsi le draggable posséderait sa "position de glissement" mais une fois en haut, il appellerait
this.props.onChange(x, y)
et reporterait la décision finale au parent. Si le parent n'aime pas l'endroit où le déplaçable s'est retrouvé, il ne mettrait tout simplement pas à jour son état, et le déplaçable "reviendrait" à sa position initiale avant de le faire glisser.Mixin ou composant?
@ssorallen a fait remarquer que, parce que "draggable" est plus un attribut qu'une chose en soi, il pourrait mieux servir de mixin. Mon expérience avec les mixins est limitée, donc je n'ai pas vu comment ils pourraient aider ou gêner dans des situations compliquées. Cela pourrait bien être la meilleure option.
la source
Mixin
classe complète puisque "Draggable" n'est pas réellement un objet, c'est une capacité d'un objet.var computedStyle = window.getComputedStyle(this.getDOMNode()); pos = { top: parseInt(computedStyle.top), left: parseInt(computedStyle.left) };
Si vous utilisez jquery avec react, vous faites probablement quelque chose de mal;) Si vous avez besoin d'un plugin jquery, je trouve qu'il est généralement plus facile et moins de code de le réécrire en pure react.this.getDOMNode().getBoundingClientRect()
- getComputedStyle peut afficher n'importe quelle propriété CSS valide, y comprisauto
dans ce cas, le code ci-dessus entraînera un fichierNaN
. Voir l'article MDN: developer.mozilla.org/en-US/docs/Web/API/Element/…this.getDOMNode()
, c'est obsolète. Utilisez une référence pour obtenir le nœud dom. facebook.github.io/react/docs/…J'ai implémenté react-dnd , un mixin HTML5 flexible par glisser-déposer pour React avec un contrôle complet du DOM.
Les bibliothèques de glisser-déposer existantes ne correspondaient pas à mon cas d'utilisation, j'ai donc écrit la mienne. C'est similaire au code que nous utilisons depuis environ un an sur Stampsy.com, mais réécrit pour tirer parti de React et Flux.
Principales exigences que j'avais:
Si cela vous semble familier, lisez la suite.
Usage
Source de glissement simple
Tout d'abord, déclarez les types de données qui peuvent être glissés.
Ceux-ci sont utilisés pour vérifier la «compatibilité» des sources de glissement et des cibles de dépôt:
(Si vous ne disposez pas de plusieurs types de données, cette bibliothèque n'est peut-être pas faite pour vous.)
Ensuite, créons un composant déplaçable très simple qui, lorsqu'il est déplacé, représente
IMAGE
:En spécifiant
configureDragDrop
, nous indiquonsDragDropMixin
le comportement glisser-déposer de ce composant. Les composants traînables et déposables utilisent le même mixin.À l'intérieur
configureDragDrop
, nous devons appelerregisterType
pour chacun de nos produits personnalisés pris en charge parItemTypes
ce composant. Par exemple, il pourrait y avoir plusieurs représentations d'images dans votre application, et chacune fournirait undragSource
pourItemTypes.IMAGE
.A
dragSource
est juste un objet spécifiant le fonctionnement de la source de glissement. Vous devez implémenterbeginDrag
pour renvoyer l'élément qui représente les données que vous faites glisser et, éventuellement, quelques options qui ajustent l'interface utilisateur de glissement. Vous pouvez éventuellement implémentercanDrag
pour interdire le glissement, ouendDrag(didDrop)
pour exécuter une logique lorsque le dépôt s'est (ou n'a pas) eu lieu. Et vous pouvez partager cette logique entre les composants en laissant un mixin partagé générerdragSource
pour eux.Enfin, vous devez utiliser
{...this.dragSourceFor(itemType)}
sur certains (un ou plusieurs) éléments dansrender
pour attacher des gestionnaires de glissement. Cela signifie que vous pouvez avoir plusieurs «poignées de glissement» dans un élément, et elles peuvent même correspondre à différents types d'éléments. (Si vous n'êtes pas familier avec la syntaxe des attributs de propagation JSX , vérifiez-la).Cible de dépôt simple
Disons que nous voulons
ImageBlock
être une cible de chute pourIMAGE
s. C'est à peu près la même chose, sauf que nous devons donnerregisterType
unedropTarget
implémentation:Faites glisser la source + déposez la cible dans un composant
Disons que nous voulons maintenant que l'utilisateur puisse faire glisser une image hors de
ImageBlock
. Nous devons juste y ajouter les éléments appropriésdragSource
et quelques gestionnaires:Quoi d'autre est possible?
Je n'ai pas tout couvert mais il est possible d'utiliser cette API de plusieurs manières supplémentaires:
getDragState(type)
etgetDropState(type)
pour savoir si le glissement est actif et utilisez-le pour basculer entre les classes CSS ou les attributs;dragPreview
d'Image
utiliser les images comme espaces réservés de glissement (utilisezImagePreloaderMixin
pour les charger);ImageBlocks
réorganisable. Nous avons seulement besoin d'eux pour mettre en œuvredropTarget
etdragSource
pourItemTypes.BLOCK
.dropTargetFor(...types)
permet de spécifier plusieurs types à la fois, de sorte qu'une zone de dépôt peut capturer de nombreux types différents.Pour obtenir une documentation et des instructions d'installation à jour, rendez -vous sur react-dnd repo sur Github .
la source
La réponse de Jared Forsyth est horriblement fausse et dépassée. Il suit tout un ensemble d'anti-modèles tels que l' utilisation de
stopPropagation
, l' initialisation de l'état à partir des accessoires , l'utilisation de jQuery, les objets imbriqués dans l'état et a undragging
champ d'état impair . En cas de réécriture, la solution sera la suivante, mais elle force toujours la réconciliation DOM virtuelle à chaque tick de déplacement de la souris et n'est pas très performante.UPD. Ma réponse était horriblement fausse et dépassée. Désormais, le code atténue les problèmes de lenteur du cycle de vie des composants React en utilisant des gestionnaires d'événements natifs et des mises à jour de style, utilise
transform
car il ne conduit pas à des reflux et limite les modifications du DOMrequestAnimationFrame
. Maintenant, c'est toujours 60 FPS pour moi dans chaque navigateur que j'ai essayé.et un peu de CSS
Exemple sur JSFiddle.
la source
J'ai mis à jour la solution polkovnikov.ph vers React 16 / ES6 avec des améliorations telles que la gestion tactile et l'alignement sur une grille, ce dont j'ai besoin pour un jeu. L'accrochage à une grille atténue les problèmes de performances.
la source
react-draggable est également facile à utiliser. Github:
https://github.com/mzabriskie/react-draggable
Et mon index.html:
Vous avez besoin de leurs styles, ce qui est court, ou vous n'obtenez pas tout à fait le comportement attendu. J'aime le comportement plus que certains des autres choix possibles, mais il y a aussi quelque chose qui s'appelle react-resizable-and-movable . J'essaie de faire travailler le redimensionnement avec draggable, mais pas de joie pour l'instant.
la source
Voici une approche simple et moderne à cela avec
useState
,useEffect
etuseRef
en ES6.la source
Je souhaite ajouter un 3ème scénario
La position de déplacement n'est en aucun cas enregistrée. Pensez-y comme un mouvement de souris - votre curseur n'est pas un composant React, non?
Tout ce que vous faites, c'est d'ajouter un accessoire comme "draggable" à votre composant et un flux des événements de glissement qui manipuleront le dom.
Dans ce cas, une manipulation DOM est une chose élégante (je n'aurais jamais pensé dire ça)
la source
setXandY
fonction avec un composant imaginaire?J'ai mis à jour la classe en utilisant des refs car toutes les solutions que je vois ici ont des choses qui ne sont plus prises en charge ou qui seront bientôt dépréciées comme
ReactDOM.findDOMNode
. Peut être parent d'un composant enfant ou d'un groupe d'enfants :)la source
Voici une réponse 2020 avec un crochet:
Puis un composant qui utilise le hook:
la source