Reputation: 31
I'm pretty new to RxJS and I'm trying to wrap my head around some concepts, but it feels like I'm still missing the right mindset.
I've been tinkering about something that feels like it should be simple, but with no success so far. I'm listening to the DOM and wrapping "keydown" and "keyup" events in Observables
const keyDown$ = Observable.fromEvent(window, 'keydown');
const keyUp$ = Observable.fromEvent(window, 'keyUp');
On the other side of this, I have a listener
keyDown$
.filter(/logic to get the right arrow keycode/)
.subscribe(downArrow => moveElementToTheRight())
The problem here is that, if you hold the key down, you'll get a value every x milliseconds, so the element is going to move very fast. This is pretty straightforward to correct
keyDown$
.filter(/logic to get the right arrow keycode/)
.sample.pipe(interval(200))
.subscribe(downArrow => moveElementToTheRight())
What I'm trying to achieve is a bit different, in that I'd want the sample size to change as the user keeps holding the key down, in a sort of ease-in fashion. For example, if the user holds the key for three seconds, then:
I've tried to think of the best approach with no success so far. My first idea was to use a map to get the count of keydown
keyDown$
.filter(/logic to get the right arrow keycode/)
.map((value, index) =>
if index < 10 return value every 500ms
if index between 10 and 20 return value every 200ms
/etc/
The problem with this is that the index will never return to 0. I then tried to find a way to reinitialise the index based on Subjects and keyUp events, but it somehow feels wrong and makes me feel like I'm not looking in the right direction.
I've added a quick JSBin of where I've gotten at, where I manage to return both the command and the duration of the keydown using a scan. I imagine I could use the switchMap statement after, but it still someone feels ... wrong ?
http://jsbin.com/jahoqocuxa/edit?js,console,output
I'd really appreciate some input from someone more knowledgeable.
Upvotes: 1
Views: 142
Reputation: 496
I think the scan
approach you took is fine, maybe a bit imperative.
I think you can model the behaviour you want quite well with the window
operator. The stream has to be chopped into windows, the separator being a keyUp$
emission. The stream is converted into a higher order Observable (Observable of Observables). So, in a map
operator you can process the individual window segments. In the example below, the first 3 emissions are mapped to A
, the rest to B
. Finally with mergeAll
the higher order Observable is flattened into an ordinary stream of A
& B
emissions.
const keyDown$ = Rx.Observable.fromEvent(window, 'keydown');
const keyUp$ = Rx.Observable.fromEvent(window, 'keyup');
keyDown$
.window(keyUp$)
.map(win => win.map((_, idx) => idx > 2 ? 'B' : 'A'))
.mergeAll();
.subscribe(console.log);
The console output when pressing a key for 2s, then releasing it and pressing it again should look like this:
"A"
"A"
"A"
"B"
"B"
"B"
"A"
"A"
"A"
"B"
"B"
Upvotes: 1