2016-09-01 6 views
0

Ich versuche Drag und Drop in RxJS zu implementieren. Ich habe einen DOM-Knoten mit der ID draggable, die herumgezogen werden kann. Die Verwendung der Standardprozedur per Drag & Drop funktioniert wie erwartet.RxJS: Erweiterte Drag & Drop - Verhindern Abonnement im Operator

Aber ich habe versucht, Drag & Drop zu verbessern und das ist, wo die Dinge kompliziert werden. Ich versuche, die Hintergrundfarbe des Elements nach dem Ziehen zu ändern und es nach dem Ablegen wieder zu ändern.

In meinem Ansatz verwende ich switchMap, um die Ergebnisse des Mausbewegungsereignisses in meine Observable abzubilden, die durch das Mouse-Down-Ereignis ausgelöst wird. Aber da ich das mouse up-Ereignis verwende, um die switchMap ed Observable (mm$ im Beispiel unten) zu vervollständigen, habe ich keine Möglichkeit, über das Abschluss-Ereignis der inneren Observablen benachrichtigt zu werden, außer wenn ich es unter dem Operator switchMap abonniere.

Ich weiß, dass das Abonnieren innerhalb eines Betreibers weit von guter Praxis entfernt ist und zu Speicherlecks führen kann. Aber was kann ich noch tun? Wie kann das besser gemacht werden?

Fiddle: https://jsfiddle.net/djwfyxs5/

const target = document.getElementById('draggable'); 
const mouseup$ = Observable.fromEvent(document, 'mouseup'); 
const mousedown$ = Observable.fromEvent(target, 'mousedown'); 
const mousemove$ = Observable.fromEvent(document, 'mousemove'); 

const move$ = mousedown$ 
    .switchMap(md => { 
    md.target.style.backgroundColor = 'yellow'; 
    const {offsetX: startX, offsetY: startY} = md; 
    const mm$ = mousemove$ 
     .map(mm => { 
     mm.preventDefault(); 
     return { 
      left: mm.clientX - startX, 
      top: mm.clientY - startY 
     }; 
     }) 
     .takeUntil(mouseup$); 

    // Can the next line be avoided? 
    mm$.subscribe(null, null,() => { 
     md.target.style.backgroundColor = 'purple'; 
    }); 

    return mm$; 
    }); 

move$.subscribe((pos) => { 
    target.style.top = pos.top + 'px'; 
    target.style.left = pos.left + 'px'; 
}); 

Antwort

1

ich hier eine ähnliche Frage beantwortet: RxJs: Drag and Drop example : add mousedragstart

Es sollte relativ einfach sein, die Antwort auf Ihre Zwecke anzupassen, da die noch Ströme, die Ereignisse enthalten, welche die Elemente aussetzen für die sie erhoben wurden.

+0

Vielen Dank für Ihre Idee zu teilen. Ich habe herausgefunden, wie ich mein Problem mit dem Entwurf in dem anderen Beitrag lösen kann. Siehe diese Geige: https://jsfiddle.net/b11gaewt/ Danke für die Antwort. Ich werde Ihre Antwort als Lösung markieren, schlage aber allen interessierten Lesern vor, sich auch meine eigene Lösung anzuschauen: http://stackoverflow.com/a/39397186/434227 – dotcs

0

Ich habe weiter mit dem Problem gekämpft, um eine Lösung zu finden. In meinem Versuch benutze ich eine Hilfsobservable, die mousedown und mouseup Ereignisse kombiniert. Durch Kombination mit dem Operator combineLatest kann auf den letzten Wert des mousedown-Ereignisses zugegriffen werden, das den angeklickten Gegenstand (Ziel) enthält.

Dies ermöglicht mir, die Farbe korrekt einzustellen, ohne das Abonnement in einer temporären Observable wie in dem Problem gesehen. Auf meine Lösung kann zugegriffen werden in this fiddle.

Ich bin nicht sicher, ob dies besser/mit weniger Code mit der gleichen Idee getan werden kann. Ich würde mich freuen, wenn möglich eine verbesserte Implementierung zu sehen.

Voll Code:

const targets = document.getElementsByClassName('draggable'); 
const arrTargets = Array.prototype.slice.call(targets); 

const mouseup$ = Rx.Observable.fromEvent(document, 'mouseup'); 
const mousedown$ = Rx.Observable.fromEvent(targets, 'mousedown'); 
const mousemove$ = Rx.Observable.fromEvent(document, 'mousemove'); 

// md  -------x------------------------------------------ 
// mm  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
// mu  -----------------------------------------x-------- 
// move$ -------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx--------- 
// s$  -------x---------------------------------x-------- 

const s$ = Rx.Observable.combineLatest(
    mousedown$, 
    mouseup$.startWith(null), // start immediately 
    (md, mu) => { 
    const { target } = md; // Target is always the one of mousedown event. 
    let type = md.type; 
    // Set type to event type of the newer event. 
    if (mu && (mu.timeStamp > md.timeStamp)) { 
     type = mu.type; 
    } 
    return { target, type }; 
    } 
); 

const move$ = mousedown$ 
    .switchMap(md => { 
    const { offsetX: startX, offsetY: startY } = md; 
    const mm$ = mousemove$ 
     .map(mm => { 
     mm.preventDefault(); 
     return { 
      left: mm.clientX - startX, 
      top: mm.clientY - startY, 
      event: mm 
     }; 
     }) 
     .takeUntil(mouseup$); 
    return mm$; 
    }); 

Rx.Observable.combineLatest(
    s$, move$.startWith(null), 
    (event, move) => { 
    let newMove = move || {}; 
    // In case we have different targets for the `event` and 
    // the `move.event` variables, the user switched the 
    // items OR the user moved the mouse too fast so that the 
    // event target is the document. 
    // In case the user switched to another element we want 
    // to ensure, that the initial position of the currently 
    // selected element is used. 
    if (move && move.event.target !== event.target && arrTargets.indexOf(move.event.target) > -1) { 
     const rect = event.target.getBoundingClientRect(); 
     newMove = { 
     top: rect.top, // + document.body.scrollTop, 
      left: rect.left // + document.body.scrollLeft 
     }; 
    } 
    return { event, move: newMove } 
    } 
) 
    .subscribe(action => { 
    const { event, move } = action; 
    if (event.type === 'mouseup') { 
     event.target.style.backgroundColor = 'purple'; 
     event.target.classList.remove('drag'); 
    } else { 
     event.target.style.backgroundColor = 'red'; 
     event.target.classList.add('drag'); 
    } 
    event.target.style.top = move.top + 'px'; 
    event.target.style.left = move.left + 'px'; 
    });