react-gamepad
Version:
A Gamepad handler as a React component
242 lines (182 loc) • 5.68 kB
JavaScript
import React from 'react'
import { XBOX } from './layouts'
class Gamepad extends React.Component {
static defaultProps = {
layout: XBOX,
stickThreshold: 0.5,
deadZone: 0.08,
gamepadIndex: 0,
onConnect: () => {},
onDisconnect: () => {},
onButtonDown: () => {},
onButtonUp: () => {},
onButtonChange: () => {},
onAxisChange: () => {},
onA: () => {},
onB: () => {},
onX: () => {},
onY: () => {},
onStart: () => {},
onBack: () => {},
onLT: () => {},
onRT: () => {},
onLB: () => {},
onRB: () => {},
onLS: () => {},
onRS: () => {},
onUp: () => {},
onDown: () => {},
onLeft: () => {},
onRight: () => {},
}
constructor(props, context) {
super(props, context)
this.padState = {
connected: false,
buttons: {
A: false,
B: false,
X: false,
Y: false,
LB: false,
LT: false,
LS: false,
RB: false,
RT: false,
RS: false,
Start: false,
Back: false,
DPadUp: false,
DPadRight: false,
DPadDown: false,
DPadLeft: false,
},
axis: {
LeftStickX: 0.0,
LeftStickY: 0.0,
RightStickX: 0.0,
RightStickY: 0.0,
RightTrigger: 0.0,
LeftTrigger: 0.0,
}
}
}
componentDidMount() {
this.mounted = true
if (window && window.requestAnimationFrame) window.requestAnimationFrame(this.updateGamepad.bind(this))
}
componentWillUnmount() {
this.mounted = false
}
updateGamepad() {
if (!this.mounted) return
const gamepadIndex = this.props.gamepadIndex
const gamepads = navigator.getGamepads()
if (gamepads.length && gamepads.length > gamepadIndex && gamepads[gamepadIndex]) {
const gamepad = gamepads[gamepadIndex]
if (!this.padState.connected) {
this.padState.connected = true
this.props.onConnect(gamepadIndex)
}
this.updateAllButtons(gamepad)
this.updateAllAxis(gamepad)
} else if (this.padState.connected) {
this.padState.connected = false
this.props.onDisconnect(gamepadIndex)
}
if (window && window.requestAnimationFrame) window.requestAnimationFrame(this.updateGamepad.bind(this))
}
updateAllButtons(gamepad) {
for (let i = 0; i < gamepad.buttons.length; ++i) {
const pressed = gamepad.buttons[i].pressed
const value = gamepad.buttons[i].value
let buttonName = this.buttonIndexToButtonName(i)
this.updateButton(buttonName, pressed)
let axisName = this.buttonIndexToAxisName(i)
this.updateAxis(axisName, value)
}
}
updateButton(buttonName, pressed) {
if (this.padState.buttons[buttonName] === undefined) {
console.error(`Unknown button ${buttonName}`)
} else if (this.padState.buttons[buttonName] !== pressed) {
this.padState.buttons[buttonName] = pressed
this.props.onButtonChange(buttonName, pressed)
this.props[`onButton${pressed ? 'Down' : 'Up'}`](buttonName)
if (pressed) this.props[`on${buttonName.replace('DPad', '')}`]()
}
}
updateAllAxis(gamepad) {
for (let i = 0; i < gamepad.axes.length; ++i) {
let axisName = this.axisIndexToAxisName(i)
const value = gamepad.axes[i]
this.updateAxis(axisName, gamepad.axes[i])
}
}
updateAxis(axisName, originalValue) {
if (axisName && originalValue !== undefined && originalValue !== null && originalValue !== NaN) {
const invert = axisName[0] === '-'
let value = originalValue * (invert ? -1 : 1)
if (Math.abs(value) < this.props.deadZone) {
value = 0
}
if (invert) axisName = axisName.substr(1)
if (this.padState.axis[axisName] !== value) {
const previousValue = this.padState.axis[axisName]
this.padState.axis[axisName] = value
this.props.onAxisChange(axisName, value, previousValue)
if (axisName === 'LeftStickX') {
if (previousValue <= this.props.stickThreshold &&
value > this.props.stickThreshold) {
this.props.onRight()
}
if (previousValue >= -this.props.stickThreshold &&
value < -this.props.stickThreshold) {
this.props.onLeft()
}
}
if (axisName === 'LeftStickY') {
if (previousValue <= this.props.stickThreshold &&
value > this.props.stickThreshold) {
this.props.onUp()
}
if (previousValue >= -this.props.stickThreshold &&
value < -this.props.stickThreshold) {
this.props.onDown()
}
}
}
}
}
buttonIndexToButtonName(index) {
const {
layout
} = this.props
if (layout.buttons && layout.buttons.length >= (index + 1)) {
return layout.buttons[index]
}
return null
}
buttonIndexToAxisName(index) {
const {
layout
} = this.props
if (layout.buttonAxis && layout.buttonAxis.length >= (index + 1)) {
return layout.buttonAxis[index]
}
return null
}
axisIndexToAxisName(index) {
const {
layout
} = this.props
if (layout.axis && layout.axis.length >= (index + 1)) {
return layout.axis[index]
}
return null
}
render() {
return React.Children.only(this.props.children)
}
}
export default Gamepad