use-control
Version:
A powerful set of hooks for elegantly handling keyboard, mouse and gamepad input (soon)
100 lines • 4.39 kB
JavaScript
import { useEffect } from 'react';
import { EMPTY, interval, merge } from 'rxjs';
import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
import { gamepadAxes$, gamepadButtonPress$, gamepadButtonReleased$ } from './input/gamepad';
import { keyEvents$ } from './input/keyboard';
import { mouseDown$, mousePosNormalisedX$, mousePosNormalisedY$, mouseUp$, whichButtonPressed, } from './input/mouse';
export function mouseButton(button) {
return { type: 'mouse-button', button: button };
}
export function keycode(code) {
return { type: 'keycode-button', code: code };
}
export function gamepadButton(controllerIndex, code) {
return { type: 'gamepad-button', controllerIndex: controllerIndex, code: code };
}
export function mouseAxis(axis) {
return { type: 'mouse-axis', axis: axis };
}
export function gamepadAxis(controllerIndex, axis, threshold) {
if (threshold === void 0) { threshold = 0.000001; }
return { type: 'gamepad-axis', controllerIndex: controllerIndex, axis: axis, threshold: threshold };
}
function buildStream(eventType, inputMap, key) {
var keys = inputMap.buttons[key];
return merge.apply(void 0, keys.map(function (k) {
switch (k.type) {
case 'gamepad-button':
var s = eventType === 'down' ? gamepadButtonPress$ : gamepadButtonReleased$;
return s.pipe(filter(function (p) { return p.buttonIndex === k.code && p.controllerIndex === k.controllerIndex; }));
case 'keycode-button':
var keyboardEventType_1 = eventType === 'down' ? 'keydown' : 'keyup';
return keyEvents$.pipe(filter(function (ev) {
return k.code === ev.keyCode &&
ev.type === keyboardEventType_1 &&
!ev.repeat;
}));
case 'mouse-button':
var m = eventType === 'down' ? mouseDown$ : mouseUp$;
return m.pipe(filter(function (ev) { return whichButtonPressed(ev) === k.button; }));
}
}));
}
function useButtonEvents(eventType, inputMap, key, sink) {
var button$ = buildStream(eventType, inputMap, key);
useEffect(function () {
var s = button$.subscribe(sink);
return function () { return s.unsubscribe(); };
}, [button$, eventType, sink]);
}
export function useButtonPressed(inputMap, key, sink) {
useButtonEvents('down', inputMap, key, sink);
}
export function useButtonReleased(inputMap, key, sink) {
useButtonEvents('up', inputMap, key, sink);
}
export function useButtonHeld(inputMap, key, intervalMs, sink) {
var keys = inputMap.buttons[key];
var pressed$ = buildStream('down', inputMap, key).pipe(map(function (_) { return 'down'; }));
var released$ = buildStream('up', inputMap, key).pipe(map(function (_) { return 'up'; }));
useEffect(function () {
var s = merge(pressed$, released$)
.pipe(switchMap(function (ev) {
if (ev === 'down') {
return interval(intervalMs);
}
else {
return EMPTY;
}
}))
.subscribe(sink);
return function () { return s.unsubscribe(); };
}, [keys, intervalMs, sink]);
}
export function useAxis(inputMap, key, sink) {
useEffect(function () {
var axes = inputMap.axes[key];
// map each axis to the relevant source stream
// & merge them together
var streams = merge.apply(void 0, axes.map(function (a) {
switch (a.type) {
case 'gamepad-axis':
return gamepadAxes$.pipe(filter(function (p) {
return p.axisIndex === a.axis &&
p.controllerIndex === a.controllerIndex &&
Math.abs(p.value) > a.threshold;
}), map(function (p) { return p.value; }));
case 'mouse-axis':
switch (a.axis) {
case 'x':
return mousePosNormalisedX$;
case 'y':
return mousePosNormalisedY$;
}
}
}));
var sub = streams.pipe(distinctUntilChanged()).subscribe(sink);
return function () { return sub.unsubscribe(); };
}, [sink]);
}
//# sourceMappingURL=actions.js.map