Hogyan lehet korlátozni egy mousemove eseményt RxJs-ben Angular 2 + Typescript használatakor?
Az RxJs tanulási folyamatban vagyok. Angular2 rc3-at használok. A következő stream működik, de túl sok mousemove eseményt ad. Szeretném lassítani őket időzítéssel (throttling) vagy egy másik vezérlő stream használatával. Hogyan tudnám ezt megtenni?
Egérmozgás-stream korlátozás nélkül
const mouseMove$ = Observable.fromEvent<MouseEvent>(document, 'mousemove');
mouseMove$.subscribe( x => console.log(x)); // works great, many {mouse position object} 's
Egyszerű megoldás: A throttling használatának valahogy így kellene kinéznie:
const mouseMove$ = Observable.fromEvent<MouseEvent>(document, 'mousemove');
const latestMouseEventEvery1Second$ = mouseMove$.sample(1000);
latestMouseEventEvery1Second$.subscribe( x => console.log(x)); // error
- ezt a sample() operátort innen vettem: http://reactivex.io/documentation/operators/sample.html
De ez nem működik Angular 2 - CLI projektben. Ez a hibát adja:
***Argument of type 'number' is not assignable to parameter of type 'Observable<\any>'*** - notice i've put <\MouseEvent> when casting.
Úgy gondolom, egy másik, erőteljesebb módja ugyanezen eredmény elérésének a következő lehet: Nagyszerű lenne, ha a legfrissebb mousemove item-et egy másik streamből kapott elemek alapján küldhetnénk. Bármelyik stream - amit mi hoztunk létre..
Például:
Amikor új elemet kapunk (1, 2, 3..) a eachSecond$ Stream-ből (=a mi „Vezérlő Streamünk”), - továbbítjuk lefelé (a mouseMoveEachSecond$-be) - a legutóbb kapott elemet a mouseMove$ stream-ből.
const eachSecond$ = Observable.timer(0, 1000); // starts at 0 and gives 1,2,3 as values each 1000 milliseconds.
const mouseMove$ = Observable.fromEvent<MouseEvent>(document, 'mousemove');
const mouseMoveEachSecond$ = Observable.merge(eachSecond$,mouseMove$)
.some magic operator I can't find()
// this could work like this:
// many mouse items are coming at a high rate from the mouse stream
// only a few items (1,2,3..) are coming form the eachSecond$ stream
//the logic: We send downstream **only one mouse item** (the last) for each new item that is coming form the eachSecond$ stream.
);
mouseMoveEachSecond$.subscribe( x => console.log(x));
// a { mouse position object } = mouse item - only when you move the mouse
// and no more than one {mouse position object} per second}
A hang egyszerű. Ezt különösen nehéznek találtam megvalósítani - amennyire tudom, ennek oka az, hogy a stream állapotát valahogyan meg kell osztani. Szükség van egy módra, hogy meg lehessen mondani, mi lett már elküldve..
A mouseMoveEachSecond$-ben az elemek egyenként, sorban jönnek. Vagy egy szám, vagy egy objektum. Tudnod kell:
- Melyik volt az utolsó
időpont elem, amikor újegér elemérkezett. - És melyik volt az utolsó
egér elem, amikor újidő elemérkezett.
Ez lehetővé teszi számodra, hogy:
- ne küldjön
egér elemetlefelé, amikor az egér nem mozog a képernyőn. - Ne küldj egy
mouse item-t downstream felé, amíg nincs újtime itemkibocsátva az eachSecond$áramból.
Ezt globális változók használatával lehet megtenni. De ez nem az RxJS módszere a dolgok elvégzésére.. Nem tudok olyan módot, amivel az utoljára elküldött elemet tárolni lehetne és a következő lépésben használni. A reduce operátor megtarthatja az állapotot: egy felszabadított elemtől a másikig, de hogyan tudnánk ezt a tulajdonságot használni, hogy ezt a viselkedést elérjük? Ezeket a stream-eket végtelennek kell tekinteni. Hmm... kellene lennie egyszerűbb módnak, hogy össze lehessen kombinálni ezt a 2 stream-et.. Lehet, hogy még korai.. Úgy érzem, az agyam nem akar Streams-ben gondolkodni :)
Tehát a 2 kérdés világosan megfogalmazódott:
- melyek azok a módok, ahogyan egy mousemove streamet korlátozni lehet Angular 2-ben?
- Használhatok egy másik streamet arra, hogy irányítsam, mikor engedem el a mousemove elemeket? Ha igen, ez a „Vezérlő stream” lehet bármilyen stream, vagy vannak korlátozások?
Nagyon köszönöm:)
Válasz #1
Olyan közel vagy! Ironikus módon a varázsoperátorod valóban a sample.
A régi mintavételi változat (RxJS4-ből) túlterhelt volt, és számot is elfogadhatott paraméterként. Az RxJS5-ben (ami az Angular2-vel érkezik) szét lett választva, így valójában két operátor létezik: sample és sampleTime. Az utóbbi az, amely időparamétert fogad, és az adott időablakban az utolsó eseményt adja ki, amikor kibocsát.
const mouseMoveEachSecond$ = mouseMove$.sampleTime(1000);
Az előbbi egy Observable-t vesz egyedüli argumentumaként, és minden alkalommal kibocsátja az utoljára kapott elemet, amikor a vezérlő Observable kibocsát.
azaz
const eachSecond$ = Observable.timer(0, 1000); // starts at 0 and gives 1,2,3 as values each 1000 milliseconds.
const mouseMove$ = Observable.fromEvent<MouseEvent>(document, 'mousemove');
const mouseMoveEachSecond$ = mouseMove$.sample(eachSecond$);
mouseMoveEachSecond$.subscribe( x => console.log(x));
Válasz #2
mi a helyzet a throttleTime importálásával:
import { throttleTime } from 'rxjs/operators';
és aztán így használd:
mousemouve$.pipe(
throttleTime(5000)
)
