typescript - RxJS6: återställ ström efter slutförande


Jag har lekt lite med RxJS 6. Jag försöker implementera dragstart, dragmove och dragend. Detta är koden jag har hittills.

import { fromEvent } from 'rxjs';
import { concatMap } from 'rxjs/internal/operators/concatMap';
import { map } from 'rxjs/internal/operators/map';
import { takeUntil } from 'rxjs/internal/operators/takeUntil';
import { repeat } from 'rxjs/internal/operators/repeat';
import { first } from 'rxjs/internal/operators/first';

const mousedown = fromEvent<MouseEvent>(window, 'mousedown');
const mousemove = fromEvent<MouseEvent>(window, 'mousemove');
const mouseup = fromEvent<MouseEvent>(window, 'mouseup');

const dragstart = mousedown.pipe(
  first()
);

const dragmove = mousedown.pipe(
  concatMap((dragStartEvent) => mousemove.pipe(
    takeUntil(mouseup))
  )
);

const dragend = mousedown.pipe(
  (dragEvent) => mouseup.pipe(first())
);

const log = (prefix: string) => (data: MouseEvent) => console.log(`${prefix}: x: ${data.clientX}, y:${data.clientY}`);

dragstart.subscribe(log('dragstart'));
dragmove.subscribe(log('dragmove'));
dragend.subscribe(log('dragend'));

Problemet är att operatorerna takeUntil och first markerar strömmen som avslutad. Det betyder att dragstart och dragend bara kommer att utlösas en gång. Finns det ett sätt att på något sätt återställa strömmen efter att denna händelse inträffar? Till exempel genom att återställa dragstart-strömmen när mouseup-strömmen tar emot en händelse.


Ytterligare förklaring

I den nuvarande implementeringen kommer dragstart och dragend loggas exakt en gång i konsolen efter att sidan har laddats (& dragning). Efter det är strömmen slutförd och inga ytterligare händelser kommer att skickas genom den. Jag vill nollställa strömmen vid varje dragoperation så att dragstart & dragend fungerar vid varje dragning istället för bara den allra första efter att sidan har laddats.

Nuvarande beteende ([] representerar musåtgärd som inträffade, kursiv text är konsolutdata):

Första dragoperationen

  • [musnedtryck]
  • dragstart
  • [muspekare]
  • dra-flytta
  • [musuppsläpp]
  • dragend

Andra dragoperationen

  • [musnedtryck]
  • (inget händer, men dragstart bör loggas till konsolen)
  • [musrörelse]
  • dra-flytta
  • [musupp]
  • (inget händer, men dragend bör loggas till konsolen)

Svar #1

Vänligen se här för ett fungerande exempel https://codepen.io/anon/pen/bMWjEVenter länkbeskrivning här

const { fromEvent } = Rx.Observable;
const target = document.querySelector('.box');

const mouseup = fromEvent(target, 'mouseup');
const mousemove = fromEvent(document, 'mousemove');
const mousedown = fromEvent(target, 'mousedown');
let log = (prefix: string, x:number, y:number) => console.log(`${prefix}: x: ${x}, y:${y}`);
const mousedrag = mousedown.selectMany((md) => {

  const startX = md.clientX + window.scrollX,
        startY = md.clientY + window.scrollY,
        startLeft = parseInt(md.target.style.left, 10) || 0,
        startTop = parseInt(md.target.style.top, 10) || 0;
  
  return mousemove.map((mm) => {
    mm.preventDefault();
   log('mousemove',mm.clientX, mm.clientY);
    return {
      left: startLeft + mm.clientX - startX,
      top: startTop + mm.clientY - startY
    };
  }).takeUntil(mouseup);
});

subscription = mousedrag.subscribe((pos) => {
  log('dragstart',pos.top, pos.left)
  target.style.top = pos.top + 'px';
  target.style.left = pos.left + 'px';
});
.box {
  position: relative;
  width: 100px;
  height: 100px;
  background: red;
  cursor: pointer;
  border:solid 10px green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.js"></script>
<div class="box"></div>

Svar #2

Men du sa att jag skulle göra det :)

Om du tar bort pipen och "first"-operatorn från dragstartströmmen

const dragstart = mousedown

strömmen kommer inte att slutföras. Dragmove kommer ändå inte att slutföras eftersom den är mappad från mousedown, inte från dragstartströmmen

Om du säger till den resulterande strömmen att bara ta den första, kommer strömmen att slutföras efter att ha skickat en gång.

Denna kod bör fungera:

const mousedown = fromEvent<MouseEvent>(window, 'mousedown');
const mousemove = fromEvent<MouseEvent>(window, 'mousemove');
const mouseup = fromEvent<MouseEvent>(window, 'mouseup');

// I dont think it's necessary to duplicate
const dragstart = mousedown

// No need to map to something else
const dragend = mouseup

const dragmove = dragstart.pipe(
  concatMapTo( 
    mousemove.pipe(
        takeUntil( dragend )
    )
  )
);



const log = (prefix: string) => (data: MouseEvent) => console.log(`${prefix}: x: ${data.clientX}, y:${data.clientY}`);

dragstart.subscribe(log('dragstart'));
dragmove.subscribe(log('dragmove'));
dragend.subscribe(log('dragend'));