typescript - RxJS6: reset aliran setelah selesai


Saya sudah bermain-main sedikit dengan RxJS 6. Saya sedang mencoba mengimplementasikan dragstart, dragmove, dan dragend. Ini adalah kode yang saya miliki sejauh ini.

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'));

Masalahnya adalah bahwa operator takeUntil dan first menandai aliran sebagai selesai. Ini berarti dragstart dan dragend hanya akan dipicu sekali. Apakah ada cara untuk mengatur ulang aliran setelah peristiwa ini terjadi? Misalnya dengan mengatur ulang aliran dragstart ketika aliran mouseup menerima peristiwa.


Penjelasan tambahan

Dalam implementasi saat ini, dragstart dan dragend akan dicatat tepat sekali di konsol setelah memuat halaman (& menyeret). Setelah itu aliran selesai dan tidak ada event lain yang akan dikirim melalui aliran tersebut. Saya ingin mengatur ulang aliran setiap operasi seret sehingga dragstart & dragend akan bekerja setiap operasi seret, bukan hanya yang pertama setelah memuat halaman.

Perilaku saat ini ([] mewakili aksi mouse yang terjadi, teks miring adalah output konsol):

Operasi seret pertama

  • [mouse down]
  • dragstart
  • [gerakkan mouse]
  • dragmove
  • [lepaskan-tombol-tikus]
  • dragend

Operasi seret kedua

  • [mouse down]
  • (tidak terjadi apa-apa, namun dragstart seharusnya dicatat di konsol)
  • [gerakkan mouse]
  • dragmove
  • [lepaskan-tombol-tikus]
  • (tidak terjadi apa-apa, namun dragend seharusnya dicatat di konsol)

Jawaban #1

Silakan lihat di sini untuk contoh yang berfungsi https://codepen.io/anon/pen/bMWjEVenter deskripsi tautan di sini

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>

Jawaban #2

Tapi kamu yang bilang untuk melakukannya :)

Jika Anda menghapus pipa dan operator "first" dari stream dragstart

const dragstart = mousedown

stream tidak akan selesai. Dragmove juga tidak akan selesai karena dipetakan dari mousedown, bukan dari stream dragstart

Jika Anda memberi tahu aliran yang dihasilkan untuk hanya mengambil yang pertama, aliran akan selesai setelah memancarkan sekali.

Kode ini seharusnya bekerja:

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'));