Ich möchte eine ziehbare (dh mit der Maus repositionierbare) React-Komponente erstellen, an der anscheinend globale Status- und Streuereignishandler beteiligt sind. Ich kann es auf schmutzige Weise tun, mit einer globalen Variablen in meiner JS-Datei, und könnte es wahrscheinlich sogar in eine schöne Abschlussoberfläche einbinden, aber ich möchte wissen, ob es einen Weg gibt, der besser mit React zusammenpasst.
Da ich dies noch nie zuvor in rohem JavaScript getan habe, würde ich gerne sehen, wie ein Experte dies tut, um sicherzustellen, dass alle Eckfälle behandelt werden, insbesondere in Bezug auf React.
Vielen Dank.
javascript
reactjs
Andrew Fleenor
quelle
quelle
Antworten:
Ich sollte dies wahrscheinlich in einen Blog-Beitrag verwandeln, aber hier ist ein ziemlich solides Beispiel.
Die Kommentare sollten die Dinge ziemlich gut erklären, aber lassen Sie mich wissen, wenn Sie Fragen haben.
Und hier ist die Geige zum Spielen: http://jsfiddle.net/Af9Jt/2/
Gedanken zum Staatseigentum usw.
"Wem sollte welcher Staat gehören" ist von Anfang an eine wichtige Frage. Im Fall einer "ziehbaren" Komponente konnte ich einige verschiedene Szenarien sehen.
Szenario 1
Der Elternteil sollte die aktuelle Position des Draggables besitzen. In diesem Fall würde der Draggable weiterhin den Status "
this.props.onChange(x, y)
Ziehe ich" besitzen, würde jedoch immer dann aufrufen, wenn ein Mausbewegungsereignis auftritt.Szenario 2
Der Elternteil muss nur die "nicht bewegte Position" besitzen, und so würde der Draggable seine "Ziehposition" besitzen, aber beim Hochfahren würde er
this.props.onChange(x, y)
die endgültige Entscheidung aufrufen und dem Elternteil aufschieben. Wenn dem übergeordneten Element nicht gefällt, wo das Draggable gelandet ist, wird sein Status einfach nicht aktualisiert, und das Draggable wird vor dem Ziehen an seiner ursprünglichen Position "zurückschnappen".Mixin oder Komponente?
@ssorallen wies darauf hin, dass "draggable" eher ein Attribut als eine Sache an sich ist und daher besser als Mixin dienen könnte. Meine Erfahrung mit Mixins ist begrenzt, daher habe ich nicht gesehen, wie sie in komplizierten Situationen helfen oder im Weg stehen könnten. Dies könnte die beste Option sein.
quelle
Mixin
als eine vollständige Klasse angemessener zu sein, da "Draggable" eigentlich kein Objekt ist, sondern eine Fähigkeit eines Objekts.var computedStyle = window.getComputedStyle(this.getDOMNode()); pos = { top: parseInt(computedStyle.top), left: parseInt(computedStyle.left) };
Wenn Sie JQuery mit React verwenden, machen Sie wahrscheinlich etwas falsch;) Wenn Sie ein JQuery-Plugin benötigen, ist es normalerweise einfacher und weniger Code, es in Pure Reag neu zu schreiben.this.getDOMNode().getBoundingClientRect()
- getComputedStyle kann jede gültige CSS-Eigenschaft ausgeben, einschließlich,auto
in welchem Fall der obige Code zu einem führtNaN
. Siehe den MDN-Artikel: developer.mozilla.org/en-US/docs/Web/API/Element/…this.getDOMNode()
, ist das veraltet. Verwenden Sie eine Referenz, um den Dom-Knoten abzurufen. facebook.github.io/react/docs/…Ich habe react-dnd implementiert , ein flexibles HTML5-Drag-and-Drop-Mixin für React mit vollständiger DOM-Steuerung.
Bestehende Drag-and-Drop-Bibliotheken passten nicht zu meinem Anwendungsfall, daher schrieb ich meine eigenen. Es ähnelt dem Code, den wir seit ungefähr einem Jahr auf Stampsy.com ausführen, wurde jedoch neu geschrieben, um die Vorteile von React und Flux zu nutzen.
Hauptanforderungen, die ich hatte:
Wenn Ihnen diese bekannt vorkommen, lesen Sie weiter.
Verwendung
Einfache Drag Source
Deklarieren Sie zunächst Datentypen, die gezogen werden können.
Diese werden verwendet, um die „Kompatibilität“ von Drag-Quellen und Drop-Zielen zu überprüfen:
(Wenn Sie nicht über mehrere Datentypen verfügen, ist diese Bibliothek möglicherweise nicht für Sie geeignet.)
Dann erstellen wir eine sehr einfache ziehbare Komponente, die beim Ziehen Folgendes darstellt
IMAGE
:Durch Angabe erklären
configureDragDrop
wirDragDropMixin
das Drag-Drop-Verhalten dieser Komponente. Sowohl ziehbare als auch ablegbare Komponenten verwenden das gleiche Mixin.Im Inneren
configureDragDrop
müssen wirregisterType
jede unserer benutzerdefiniertenItemTypes
Komponenten aufrufen , die von der Komponente unterstützt werden. Beispielsweise enthält Ihre App möglicherweise mehrere Darstellungen von Bildern, von denen jede eindragSource
für bereitstelltItemTypes.IMAGE
.A
dragSource
ist nur ein Objekt, das angibt, wie die Drag-Quelle funktioniert. Sie müssen implementierenbeginDrag
, um ein Element zurückzugeben, das die Daten darstellt, die Sie ziehen, und optional einige Optionen, die die Benutzeroberfläche zum Ziehen anpassen. Sie können optional implementieren,canDrag
um das Ziehen zu verbieten oderendDrag(didDrop)
eine Logik auszuführen, wenn der Abwurf aufgetreten ist (oder nicht). Und Sie können diese Logik zwischen Komponenten teilen, indem Sie ein gemeinsames MixindragSource
für sie generieren lassen.Schließlich müssen Sie
{...this.dragSourceFor(itemType)}
einige (ein oder mehrere) Elemente verwendenrender
, um Drag-Handler anzuhängen. Dies bedeutet, dass Sie mehrere „Ziehpunkte“ in einem Element haben können und diese sogar verschiedenen Elementtypen entsprechen können. (Wenn Sie mit der Syntax von JSX Spread Attributes nicht vertraut sind, lesen Sie sie durch.)Einfaches Drop-Ziel
Nehmen wir
ImageBlock
an , wir wollen ein Drop-Ziel fürIMAGE
s sein. Es ist ziemlich ähnlich, außer dass wirregisterType
einedropTarget
Implementierung geben müssen:Quelle ziehen + Ziel in einer Komponente ablegen
Angenommen, wir möchten jetzt, dass der Benutzer ein Bild herausziehen kann
ImageBlock
. Wir müssen nurdragSource
ein paar entsprechende Handler hinzufügen :Was ist sonst noch möglich?
Ich habe nicht alles behandelt, aber es ist möglich, diese API auf einige weitere Arten zu verwenden:
getDragState(type)
undgetDropState(type)
, um zu erfahren, ob das Ziehen aktiv ist, und um CSS-Klassen oder -Attribute umzuschalten.dragPreview
anImage
, dass Bilder als Drag-Platzhalter verwendet werden sollen (ImagePreloaderMixin
zum Laden).ImageBlocks
nachbestellbar machen . Wir brauchen sie nur zu implementierendropTarget
unddragSource
fürItemTypes.BLOCK
.dropTargetFor(...types)
Ermöglicht die gleichzeitige Angabe mehrerer Typen, sodass eine Drop-Zone viele verschiedene Typen erfassen kann.Aktuelle Dokumentationen und Installationsanweisungen finden Sie unter Reagieren und Repo auf Github .
quelle
Die Antwort von Jared Forsyth ist schrecklich falsch und veraltet. Es folgt eine ganze Reihe von Antimustern wie die Verwendung von
stopPropagation
, das Initialisieren des Status von Requisiten , die Verwendung von jQuery, verschachtelte Objekte im Status und ein ungeradesdragging
Statusfeld. Wenn es neu geschrieben wird, lautet die Lösung wie folgt, erzwingt jedoch weiterhin die virtuelle DOM-Abstimmung bei jedem Mausklick und ist nicht sehr performant.UPD. Meine Antwort war schrecklich falsch und veraltet. Jetzt
transform
verringert der Code Probleme des langsamen Lebenszyklus von React-Komponenten durch die Verwendung nativer Ereignishandler und Stilaktualisierungen, verwendet, da dies nicht zu Rückflüssen führt, und drosselt DOM-ÄnderungenrequestAnimationFrame
. Jetzt sind es in jedem Browser, den ich ausprobiert habe, konstant 60 FPS für mich.und ein bisschen CSS
Beispiel für JSFiddle.
quelle
Ich habe die polkovnikov.ph-Lösung auf React 16 / ES6 mit Verbesserungen wie Touch-Handling und Einrasten in ein Raster aktualisiert, was ich für ein Spiel benötige. Durch das Einrasten in ein Raster werden die Leistungsprobleme verringert.
quelle
React-Draggable ist ebenfalls einfach zu bedienen. Github:
https://github.com/mzabriskie/react-draggable
Und meine index.html:
Sie brauchen ihre Stile, was kurz ist, oder Sie bekommen nicht ganz das erwartete Verhalten. Ich mag das Verhalten mehr als einige der anderen möglichen Entscheidungen, aber es gibt auch etwas, das als reaktionsänderbar und beweglich bezeichnet wird . Ich versuche, die Größe mit Draggable zu ändern, aber bisher keine Freude.
quelle
Hier ist eine einfache moderne Annäherung an diese mit
useState
,useEffect
unduseRef
in ES6.quelle
Ich möchte ein drittes Szenario hinzufügen
Die Bewegungsposition wird in keiner Weise gespeichert. Stellen Sie sich das als Mausbewegung vor - Ihr Cursor ist keine Reaktionskomponente, oder?
Alles, was Sie tun, ist, Ihrer Komponente eine Requisite wie "ziehbar" und einen Stream der Ziehereignisse hinzuzufügen, die den Dom manipulieren.
In diesem Fall ist eine DOM-Manipulation eine elegante Sache (ich hätte nie gedacht, dass ich das sagen würde)
quelle
setXandY
Funktion mit einer imaginären Komponente füllen ?Ich habe die Klasse mithilfe von Refs aktualisiert, da alle Lösungen, die ich hier sehe, Dinge enthalten, die nicht mehr unterstützt werden oder bald abgeschrieben werden
ReactDOM.findDOMNode
. Kann Eltern einer untergeordneten Komponente oder einer Gruppe von Kindern sein :)quelle