let currentInputs={}; let handlers=[]; let config; const toAngleMagnitude=(x, y) => { return { angle: ((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI, magnitude: Math.hypot(x, y) }; }; const handleAngleMagnitude=(x, y, threshold=0, fn=null, clearBuffer=false) => { const {angle, magnitude}=toAngleMagnitude(x, y); if(magnitude>threshold) { let inputs=currentInputs; if(angle>.25 && angle <.75) inputs.right=true; else if(angle>.75 && angle<1.25) inputs.up=true; else if(angle>1.25 && angle<1.75) inputs.left=true; else inputs.down=true; if(clearBuffer) inputs.clearBuffer=true; if(fn) fn(angle, magnitude); } } const handleCrosspad=(() => { const fn=e => handleAngleMagnitude( e.touches[0].clientX-window.innerWidth/2, e.touches[0].clientY-window.innerHeight/2, 0, null, !config.touchscreen.buffer ); return { touchstart: fn, touchmove: fn }; })(); const handleKeyboard={ keydown: e => { let inputs=currentInputs; if(e.key=='ArrowUp') inputs.up=true; else if(e.key=='ArrowDown') inputs.down=true; else if(e.key=='ArrowLeft') inputs.left=true; else if(e.key=='ArrowRight') inputs.right=true; if(!config.keyboard.buffer) inputs.clearBuffer=true; } }; const handleJoystick=(() => { let center={ x: 0, y: 0 }; return { touchstart: e => { center.x=e.touches[0].clientX; center.y=e.touches[0].clientY; }, touchmove: e => handleAngleMagnitude( e.touches[0].clientX-center.x, e.touches[0].clientY-center.y, config.touchscreen.deadzone, null, !config.touchscreen.buffer ) } })(); const handleSwipe=(() => { let center={ x: 0, y: 0 }; let resetCenter=e => { center.x=e.touches[0].clientX; center.y=e.touches[0].clientY; }; return { touchstart: resetCenter, touchmove: e => handleAngleMagnitude( e.touches[0].clientX-center.x, e.touches[0].clientY-center.y, config.touchscreen.deadzone, () => resetCenter(e), !config.touchscreen.buffer ) } })(); const handleGamepads={ frame: () => { const gp=navigator.getGamepads()[0]; let inputs=currentInputs; if(!gp || !gp.axes) return; handleAngleMagnitude( gp.axes[0], gp.axes[1], config.gamepad.deadzone, null, !config.gamepad.buffer ); } }; const handleEvent=(type, evt) => { for(let handler of handlers) { let fn=handler[type]; if(fn) fn(evt); } }; const enableHandler=handler => { if(!handlers.includes(handler)) handlers.push(handler); }; const disableHandler=handler => { let idx=handlers.indexOf(handler); if(idx!=-1) handlers.splice(idx, 1); }; const updateConfig=cfg => config=cfg; const clear=() => Object .keys(currentInputs) .forEach(key => delete currentInputs[key]); for(let type of ['keydown', 'touchstart', 'touchmove']) { window.addEventListener(type, handleEvent.bind(null, type)); } return module.exports={ inputs: currentInputs, clear, enableHandler, disableHandler, framefn: handleEvent.bind(null, 'frame'), availableHandlers: { keyboard: handleKeyboard, gamepad: handleGamepads, touchscreenCrosspad: handleCrosspad, touchscreenJoystick: handleJoystick, touchscreenSwipe: handleSwipe }, updateConfig };