2020-04-06 08:58:44 +00:00
|
|
|
const config=require('config');
|
|
|
|
|
2020-04-05 14:14:39 +00:00
|
|
|
let currentInputs={};
|
|
|
|
let handlers=[];
|
2020-04-05 18:58:35 +00:00
|
|
|
let hud;
|
2020-04-05 14:14:39 +00:00
|
|
|
|
|
|
|
const toAngleMagnitude=(x, y) => {
|
|
|
|
return {
|
|
|
|
angle: ((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI,
|
|
|
|
magnitude: Math.hypot(x, y)
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-04-06 08:58:44 +00:00
|
|
|
const handleAngleMagnitude=(x, y, threshold=0, fn=null) => {
|
2020-04-05 14:14:39 +00:00
|
|
|
const {angle, magnitude}=toAngleMagnitude(x, y);
|
|
|
|
|
|
|
|
if(magnitude>threshold) {
|
2020-04-05 16:22:37 +00:00
|
|
|
let inputs=currentInputs;
|
2020-04-05 14:14:39 +00:00
|
|
|
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(fn) fn(angle, magnitude);
|
|
|
|
}
|
2020-04-06 12:01:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const removeChild=(parent, child) => {
|
|
|
|
if(child.parentNode==parent) parent.removeChild(child);
|
|
|
|
};
|
2020-04-05 14:14:39 +00:00
|
|
|
|
|
|
|
const handleCrosspad=(() => {
|
2020-04-05 18:58:35 +00:00
|
|
|
const ns='http://www.w3.org/2000/svg';
|
|
|
|
const cross=document.createElementNS(ns, 'svg');
|
|
|
|
cross.classList.add('crosspadOverlay');
|
|
|
|
cross.setAttribute('width', 1000);
|
|
|
|
cross.setAttribute('height', 1000);
|
|
|
|
let dr=document.createElementNS(ns, 'line');
|
|
|
|
dr.setAttribute('x1', 0);
|
|
|
|
dr.setAttribute('y1', 0);
|
|
|
|
dr.setAttribute('x2', 1000);
|
|
|
|
dr.setAttribute('y2', 1000);
|
|
|
|
dr.setAttribute('stroke', 'black');
|
|
|
|
cross.appendChild(dr);
|
|
|
|
let dl=document.createElementNS(ns, 'line');
|
|
|
|
dl.setAttribute('x1', 1000);
|
|
|
|
dl.setAttribute('y1', 0);
|
|
|
|
dl.setAttribute('x2', 0);
|
|
|
|
dl.setAttribute('y2', 1000);
|
|
|
|
dl.setAttribute('stroke', 'black');
|
|
|
|
cross.appendChild(dl);
|
|
|
|
|
2020-04-06 08:58:44 +00:00
|
|
|
let useOverlay=false;
|
|
|
|
let enabled=false;
|
|
|
|
const displayOverlay=() => {
|
2020-04-06 09:49:36 +00:00
|
|
|
if(useOverlay && enabled) hud.appendChild(cross);
|
2020-04-06 12:01:08 +00:00
|
|
|
else removeChild(hud, cross);
|
2020-04-06 08:58:44 +00:00
|
|
|
};
|
|
|
|
config.watchB('input.touchscreen.crosspad.overlay', (k, v) => {
|
|
|
|
useOverlay=v;
|
|
|
|
displayOverlay();
|
|
|
|
});
|
|
|
|
|
2020-04-05 14:14:39 +00:00
|
|
|
const fn=e =>
|
|
|
|
handleAngleMagnitude(
|
|
|
|
e.touches[0].clientX-window.innerWidth/2,
|
|
|
|
e.touches[0].clientY-window.innerHeight/2,
|
|
|
|
0,
|
2020-04-06 08:58:44 +00:00
|
|
|
null
|
2020-04-05 14:14:39 +00:00
|
|
|
);
|
2020-04-06 08:58:44 +00:00
|
|
|
|
|
|
|
const init=() => {
|
|
|
|
useOverlay=config.getB('input.touchscreen.crosspad.overlay');
|
|
|
|
enabled=true;
|
|
|
|
displayOverlay();
|
|
|
|
};
|
|
|
|
const fini=() => {
|
|
|
|
enabled=false;
|
|
|
|
displayOverlay();
|
|
|
|
};
|
|
|
|
|
2020-04-05 14:14:39 +00:00
|
|
|
return {
|
|
|
|
touchstart: fn,
|
2020-04-05 18:58:35 +00:00
|
|
|
touchmove: fn,
|
2020-04-06 09:49:36 +00:00
|
|
|
init, fini
|
2020-04-05 14:14:39 +00:00
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleJoystick=(() => {
|
2020-04-06 09:49:36 +00:00
|
|
|
let cvs=document.createElement('canvas');
|
|
|
|
cvs.classList.add('joystickOverlay');
|
|
|
|
let ctx=cvs.getContext('2d');
|
|
|
|
let enabled=false;
|
|
|
|
let useOverlay=false;
|
2020-04-06 12:01:08 +00:00
|
|
|
let firstTouch=false;
|
2020-04-06 09:49:36 +00:00
|
|
|
|
2020-04-05 14:14:39 +00:00
|
|
|
let center={
|
|
|
|
x: 0,
|
|
|
|
y: 0
|
|
|
|
};
|
2020-04-06 08:58:44 +00:00
|
|
|
let deadzone;
|
|
|
|
|
2020-04-06 09:49:36 +00:00
|
|
|
const displayOverlay=() => {
|
2020-04-06 12:01:08 +00:00
|
|
|
if(!enabled || !useOverlay || !firstTouch) return removeChild(hud, cvs);
|
2020-04-06 09:49:36 +00:00
|
|
|
|
|
|
|
cvs.width=cvs.height=4*deadzone+120;
|
|
|
|
hud.appendChild(cvs);
|
|
|
|
ctx.clearRect(0, 0, cvs.width, cvs.width);
|
|
|
|
|
|
|
|
ctx.strokeStyle='black';
|
|
|
|
ctx.lineWidth=1;
|
|
|
|
ctx.beginPath();
|
|
|
|
const rad=2*deadzone+50;
|
|
|
|
ctx.moveTo(rad*Math.cos(Math.PI/4)+cvs.width/2, rad*Math.sin(Math.PI/4)+cvs.width/2);
|
|
|
|
ctx.lineTo(rad*Math.cos(Math.PI/4+Math.PI)+cvs.width/2, rad*Math.sin(Math.PI/4+Math.PI)+cvs.width/2);
|
|
|
|
ctx.moveTo(rad*Math.cos(-Math.PI/4)+cvs.width/2, rad*Math.sin(-Math.PI/4)+cvs.width/2);
|
|
|
|
ctx.lineTo(rad*Math.cos(-Math.PI/4+Math.PI)+cvs.width/2, rad*Math.sin(-Math.PI/4+Math.PI)+cvs.width/2);
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
ctx.strokeStyle='gray';
|
|
|
|
ctx.lineWidth=2;
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.ellipse(cvs.width/2, cvs.width/2, deadzone, deadzone, 0, 0, Math.PI*2);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.lineWidth=3;
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.ellipse(cvs.width/2, cvs.width/2, deadzone*2+50, deadzone*2+50, 0, 0, Math.PI*2);
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
cvs.style.left=center.x+'px';
|
|
|
|
cvs.style.top=center.y+'px';
|
|
|
|
};
|
|
|
|
|
2020-04-06 08:58:44 +00:00
|
|
|
const init=() => {
|
2020-04-06 09:49:36 +00:00
|
|
|
enabled=true;
|
2020-04-06 08:58:44 +00:00
|
|
|
deadzone=config.getN('input.touchscreen.joystick.deadzone');
|
2020-04-06 09:49:36 +00:00
|
|
|
useOverlay=config.getB('input.touchscreen.joystick.overlay');
|
|
|
|
displayOverlay();
|
|
|
|
};
|
|
|
|
const fini=() => {
|
|
|
|
enabled=false;
|
|
|
|
displayOverlay();
|
2020-04-06 08:58:44 +00:00
|
|
|
};
|
|
|
|
config.watchN('input.touchscreen.joystick.deadzone', (k, v) => {
|
|
|
|
deadzone=v;
|
2020-04-06 09:49:36 +00:00
|
|
|
displayOverlay();
|
|
|
|
});
|
|
|
|
config.watchB('input.touchscreen.joystick.overlay', (k, v) => {
|
|
|
|
useOverlay=v;
|
|
|
|
displayOverlay();
|
2020-04-06 08:58:44 +00:00
|
|
|
});
|
|
|
|
|
2020-04-05 14:14:39 +00:00
|
|
|
return {
|
2020-04-06 09:49:36 +00:00
|
|
|
init, fini,
|
2020-04-05 14:14:39 +00:00
|
|
|
touchstart: e => {
|
|
|
|
center.x=e.touches[0].clientX;
|
|
|
|
center.y=e.touches[0].clientY;
|
2020-04-06 12:01:08 +00:00
|
|
|
firstTouch=true;
|
2020-04-06 09:49:36 +00:00
|
|
|
displayOverlay();
|
2020-04-05 14:14:39 +00:00
|
|
|
},
|
|
|
|
touchmove: e =>
|
|
|
|
handleAngleMagnitude(
|
|
|
|
e.touches[0].clientX-center.x,
|
|
|
|
e.touches[0].clientY-center.y,
|
2020-04-06 08:58:44 +00:00
|
|
|
deadzone,
|
|
|
|
null
|
2020-04-05 14:14:39 +00:00
|
|
|
)
|
|
|
|
}
|
2020-04-05 16:22:37 +00:00
|
|
|
})();
|
2020-04-05 14:14:39 +00:00
|
|
|
|
|
|
|
const handleSwipe=(() => {
|
|
|
|
let center={
|
|
|
|
x: 0,
|
|
|
|
y: 0
|
|
|
|
};
|
2020-04-06 08:58:44 +00:00
|
|
|
let deadzone;
|
|
|
|
|
2020-04-05 14:14:39 +00:00
|
|
|
let resetCenter=e => {
|
|
|
|
center.x=e.touches[0].clientX;
|
|
|
|
center.y=e.touches[0].clientY;
|
|
|
|
};
|
2020-04-06 08:58:44 +00:00
|
|
|
|
|
|
|
const init=() => {
|
|
|
|
deadzone=config.getN('input.touchscreen.swipe.deadzone');
|
|
|
|
};
|
|
|
|
config.watchN('input.touchscreen.swipe.deadzone', (k, v) => {
|
|
|
|
deadzone=v;
|
|
|
|
});
|
|
|
|
|
2020-04-05 14:14:39 +00:00
|
|
|
return {
|
2020-04-06 08:58:44 +00:00
|
|
|
init,
|
2020-04-05 14:14:39 +00:00
|
|
|
touchstart: resetCenter,
|
|
|
|
touchmove: e =>
|
|
|
|
handleAngleMagnitude(
|
|
|
|
e.touches[0].clientX-center.x,
|
|
|
|
e.touches[0].clientY-center.y,
|
2020-04-06 08:58:44 +00:00
|
|
|
deadzone,
|
|
|
|
() => resetCenter(e)
|
2020-04-05 14:14:39 +00:00
|
|
|
)
|
|
|
|
}
|
2020-04-05 16:22:37 +00:00
|
|
|
})();
|
2020-04-05 14:14:39 +00:00
|
|
|
|
2020-04-06 08:58:44 +00:00
|
|
|
const handleGamepads=(() => {
|
|
|
|
let deadzone;
|
2020-04-05 14:14:39 +00:00
|
|
|
|
2020-04-06 08:58:44 +00:00
|
|
|
const init=() => {
|
|
|
|
deadzone=config.getN('input.touchscreen.swipe.deadzone');
|
|
|
|
};
|
|
|
|
config.watchN('input.touchscreen.swipe.deadzone', (k, v) => {
|
|
|
|
deadzone=v;
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
init,
|
|
|
|
frame: () => {
|
|
|
|
const gp=navigator.getGamepads()[0];
|
|
|
|
let inputs=currentInputs;
|
|
|
|
if(!gp || !gp.axes) return;
|
|
|
|
|
|
|
|
handleAngleMagnitude(
|
|
|
|
gp.axes[0],
|
|
|
|
gp.axes[1],
|
|
|
|
deadzone,
|
|
|
|
null
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
})();
|
2020-04-05 14:14:39 +00:00
|
|
|
|
|
|
|
const handleEvent=(type, evt) => {
|
|
|
|
for(let handler of handlers) {
|
|
|
|
let fn=handler[type];
|
|
|
|
if(fn) fn(evt);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const enableHandler=handler => {
|
2020-04-05 18:58:35 +00:00
|
|
|
if(!handlers.includes(handler)) {
|
|
|
|
handlers.push(handler);
|
|
|
|
if(handler.init) handler.init();
|
|
|
|
}
|
2020-04-05 14:14:39 +00:00
|
|
|
};
|
|
|
|
const disableHandler=handler => {
|
|
|
|
let idx=handlers.indexOf(handler);
|
2020-04-05 18:58:35 +00:00
|
|
|
if(idx!=-1) {
|
|
|
|
handlers.splice(idx, 1);
|
|
|
|
if(handler.fini) handler.fini();
|
|
|
|
}
|
2020-04-05 14:14:39 +00:00
|
|
|
};
|
2020-04-06 08:58:44 +00:00
|
|
|
const linkHandler=(handler, key) => {
|
|
|
|
if(config.getB(key)) enableHandler(handler);
|
|
|
|
config.watchB(key, (k, v) => {
|
|
|
|
if(v) enableHandler(handler);
|
|
|
|
else disableHandler(handler);
|
|
|
|
});
|
|
|
|
};
|
2020-04-05 14:14:39 +00:00
|
|
|
|
2020-04-06 08:58:44 +00:00
|
|
|
const init=({hud: hudElem}) => {
|
|
|
|
hud=hudElem;
|
|
|
|
linkHandler(handleCrosspad, 'input.touchscreen.crosspad.enabled');
|
|
|
|
linkHandler(handleJoystick, 'input.touchscreen.joystick.enabled');
|
|
|
|
linkHandler(handleSwipe, 'input.touchscreen.swipe.enabled');
|
|
|
|
linkHandler(handleGamepads, 'input.gamepad.enabled');
|
|
|
|
linkHandler(handleKeyboard, 'input.keyboard.enabled');
|
|
|
|
};
|
2020-04-05 14:14:39 +00:00
|
|
|
|
|
|
|
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,
|
|
|
|
framefn: handleEvent.bind(null, 'frame'),
|
2020-04-06 08:58:44 +00:00
|
|
|
init
|
2020-04-05 14:14:39 +00:00
|
|
|
};
|