-
-
Notifications
You must be signed in to change notification settings - Fork 150
rstream recipes
Metastreams are a powerful tool to build various higher-order stream operators/constructs. Here's an example to create a resettable countdown sequence...
import { fromInterval, trace } from "@thi.ng/rstream";
import { reducer, scan } from "@thi.ng/transducers";
const countdown = (from: number, delay = 1000) =>
metaStream(() => fromIterable(range(from, -1), delay));
// countdown from 10 @ 1 Hz
const a = countdown(10, 1);
a.subscribe(trace());
// kick off
a.next();
// 10
// 9
// 8
// 7
// reset
a.next();
// 10
// ...
// 0
// can be re-triggered any time
a.next();
// 10
// 9
// ...
Alternatively one can similarly build a resettable timeout utility stream, e.g. as sidechain/control input for other stream constructs. Each time a value is being sent to the stream, the timeout resets and once triggered, emits the user defined value.
const timeout = <T>(value: T, delay = 1000) =>
metaStream(() => fromIterable([value], delay));
const a = timeout("foo", 1000);
a.subscribe(trace());
// kick off
a.next();
// 1sec later
// foo
The tween operator takes an existing stream src
and attaches a new subscription which interpolates between incoming values from src
using the given mix
function. The returned subscription produces
values at a fixed frequency, defined by delay
(in ms, default 16ms). In general, that frequency should be higher than that of src
.
If stop
is given as well, no values will be passed downstream if that function returns true. This can be used to limit traffic once the tween target value has been reached.
import { stream, tween, trace } from "@thi.ng/rstream";
// input stream
const val = stream<number>();
tween(
// consume from `val` stream
val,
// initial start value to interpolate from
0,
// interpolation fn (LERP)
(a, b) => a + (b - a) * 0.5,
// stop emitting values if difference to previous result < 0.01
(a, b) => Math.abs(a - b) < 0.01,
16
).subscribe(trace());
// kick off
val.next(10)
// 5
// 7.5
// ...
// 9.98046875
val.next(100)
// 55
// 77.5
// ...
// 99.989013671875
For games and other interactive applications needing to track the state of multiple keys on the keyboard, the following function provides a stream of key state objects representing the pressed state of selected keys (and only those). The returned construct is merging two keyboard event streams attached to target
(e.g. window
) and then immutably updates and emits a key state object with each qualifying keydown
or keyup
event.
import { fromEvent, merge } from "@thi.ng/rstream";
import { comp, filter, reducer, scan } from "@thi.ng/transducers";
const keyStateStream = (target: EventTarget, keys: Set<string>) =>
merge({
src: [
fromEvent(target, "keydown"),
fromEvent(target, "keyup")
],
xform: comp(
filter((e: KeyboardEvent) => keys.has(e.key.toLowerCase())),
scan(
reducer(
() => ({}),
(acc, e) => ({
...acc,
[e.key.toLowerCase()]: e.type !== "keyup"
})
)
)
)
});
// create a stream of key states for the given keys
keyStateStream(new Set(["w", "a", "s", "d", "shift", "alt"]))
.subscribe({ next: console.log });
// { shift: true }
// { shift: true, w: true }
// { shift: true, w: false }
// { shift: false, w: false }
// { shift: false, w: false, a: true }
// { shift: false, w: false, a: false }