glodrei
Version:
useful add-ons for react-three-fiber
92 lines (77 loc) • 2.49 kB
text/mdx
title: KeyboardControls
sourcecode: src/web/KeyboardControls.tsx

<Grid cols={4}>
<li>
<Codesandbox id="vkgi6" />
</li>
</Grid>
A rudimentary keyboard controller which distributes your defined data-model to the `useKeyboard` hook. It's a rather simple way to get started with keyboard input.
```tsx
type KeyboardControlsState<T extends string = string> = { [K in T]: boolean }
type KeyboardControlsEntry<T extends string = string> = {
/** Name of the action */
name: T
/** The keys that define it, you can use either event.key, or event.code */
keys: string[]
/** If the event receives the keyup event, true by default */
up?: boolean
}
type KeyboardControlsProps = {
/** A map of named keys */
map: KeyboardControlsEntry[]
/** All children will be able to useKeyboardControls */
children: React.ReactNode
/** Optional onchange event */
onChange: (name: string, pressed: boolean, state: KeyboardControlsState) => void
/** Optional event source */
domElement?: HTMLElement
}
```
You start by wrapping your app, or scene, into `<KeyboardControls>`.
```tsx
enum Controls {
forward = 'forward',
back = 'back',
left = 'left',
right = 'right',
jump = 'jump',
}
function App() {
const map = useMemo<KeyboardControlsEntry<Controls>[]>(()=>[
{ name: Controls.forward, keys: ['ArrowUp', 'KeyW'] },
{ name: Controls.back, keys: ['ArrowDown', 'KeyS'] },
{ name: Controls.left, keys: ['ArrowLeft', 'KeyA'] },
{ name: Controls.right, keys: ['ArrowRight', 'KeyD'] },
{ name: Controls.jump, keys: ['Space'] },
], [])
return (
<KeyboardControls map={map}>
<App />
</KeyboardControls>
```
You can either respond to input reactively, it uses zustand (with the `subscribeWithSelector` middleware) so all the rules apply:
```tsx
function Foo() {
const forwardPressed = useKeyboardControls<Controls>(state => state.forward)
```
Or transiently, either by `subscribe`, which is a function which returns a function to unsubscribe, so you can pair it with useEffect for cleanup, or `get`, which fetches fresh state non-reactively.
```tsx
function Foo() {
const [sub, get] = useKeyboardControls<Controls>()
useEffect(() => {
return sub(
(state) => state.forward,
(pressed) => {
console.log('forward', pressed)
}
)
}, [])
useFrame(() => {
// Fetch fresh data from store
const pressed = get().back
})
}
```