Last active
November 25, 2018 21:21
-
-
Save Dinir/a13650869b2d8f697399ddc872c35d10 to your computer and use it in GitHub Desktop.
Simple 4-button Gamepad Input Display, made as a makeshift tool, so maybe only works with the first pad connected. π
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable no-undef */ | |
window.onload = function () { | |
const axisDom = document.getElementsByClassName('analogLeft')[0] | |
const dpadDoms = document.getElementsByClassName('dpad')[0] | |
const buttonDoms = document.getElementsByClassName('buttons')[0] | |
const cos45 = Math.cos(1 / 4 * Math.PI) | |
let mapping = { | |
analogLeft: [0, 1], // hori, vert | |
dpad: [12, 13, 14, 15], // up, down, left, right | |
buttons: [2, 0, 3, 1, 9] // four buttons and start | |
} | |
let useAnalog = false | |
let mappingNow = false | |
const findPressedInput = (() => { | |
let previouslyPressed = [] | |
const isAlreadyPressed = newPress => { | |
return previouslyPressed[0] === newPress[0] && | |
previouslyPressed[1] === newPress[1] | |
} | |
return (gamepad) => { | |
/* | |
in linux some shoulder triggers stay at -1.0 when not touched, | |
and it triggers the logic as being pressed. | |
Since this input display is meant to be used as a joystick-like | |
layout, and joysticks and joypads I used often has left analog stick | |
connected to first two axes, I decided to use only the axes. | |
*/ | |
const axes = gamepad.axes.slice(0, 2) | |
const buttons = gamepad.buttons.map(v => v.value) | |
let pressed = [] | |
axes.some((v, i) => { | |
if (Math.abs(v) > 0.5) { | |
pressed = pressed.concat(['a', i]) | |
return true | |
} | |
}) | |
if (!pressed.length) { | |
buttons.some((v, i) => { | |
if (Math.abs(v) === 1) { | |
pressed = pressed.concat(['b', i]) | |
return true | |
} | |
}) | |
} | |
if (pressed.length) { | |
if (isAlreadyPressed(pressed)) { | |
return false | |
} else { | |
previouslyPressed = pressed | |
console.log(pressed) | |
return pressed | |
} | |
} else { | |
if (previouslyPressed.length) { | |
previouslyPressed = [] | |
} | |
return false | |
} | |
} | |
})() | |
const isRightClick = e => { | |
e = e || window.event | |
if ('which' in e) { | |
return e.which === 3 | |
} else if ('button' in e) { | |
return e.button === 2 | |
} | |
} | |
const countNulls = array => { | |
let count = 0 | |
for (let i = 0; i < array.length; i++) { | |
count += array[i] === null | |
} | |
return count | |
} | |
const addClass = (dom, className) => { | |
const oldClassName = dom.getAttribute('class') || '' | |
if (oldClassName.indexOf(className) === -1) { | |
dom.setAttribute( | |
'class', | |
`${oldClassName} ${className}` | |
) | |
} | |
} | |
const removeClass = (dom, className) => { | |
dom.setAttribute( | |
'class', | |
dom.getAttribute('class').replace(` ${className}`, '') | |
) | |
} | |
rAF(() => handler.refresh(gamepads)) | |
const gpDomSet = (() => { | |
const analogLeftJog = axisDom.children[0] | |
const buttons = buttonDoms.children | |
const setters = { | |
analogLeft: (...values) => { | |
analogLeftJog.style.left = (0.75 + (0.25 * values[0])) + 'em' | |
analogLeftJog.style.top = (0.75 + (0.25 * values[1])) + 'em' | |
return setters | |
}, | |
dpad: (...values) => { | |
const valuesNumber = values.map(v => v.value) | |
const vert = (-1 * valuesNumber[0] + valuesNumber[1]) * | |
Math.min(1, (valuesNumber[2] + valuesNumber[3] === 0) + cos45) | |
const hori = (-1 * valuesNumber[2] + valuesNumber[3]) * | |
Math.min(1, (valuesNumber[0] + valuesNumber[1] === 0) + cos45) | |
analogLeftJog.style.top = (0.75 + (0.25 * vert)) + 'em' | |
analogLeftJog.style.left = (0.75 + (0.25 * hori)) + 'em' | |
return setters | |
}, | |
buttons: (...values) => { | |
values.forEach((v, i) => { | |
if (buttons[i]) { | |
buttons[i].className = values[i].pressed ? 'pressed' : '' | |
} | |
}) | |
return setters | |
} | |
} | |
return setters | |
})() | |
handler.update = () => { | |
if (mappingNow) { | |
let pressedInput = findPressedInput(gamepads[0]) | |
if (mappingNow === 'axis') { | |
const analogLeftJog = axisDom.children[0] | |
const buttonAmount = mapping.analogLeft.length | |
const buttonToMap = buttonAmount - countNulls(mapping.analogLeft) | |
switch (buttonToMap) { | |
case 0: | |
addClass(analogLeftJog, 'mapping') | |
analogLeftJog.innerText = 'β' | |
break | |
case 1: | |
analogLeftJog.innerText = 'β' | |
break | |
default: | |
removeClass(analogLeftJog, 'mapping') | |
analogLeftJog.innerText = '' | |
mappingNow = false | |
} | |
if (mappingNow && pressedInput) { | |
mapping.analogLeft[buttonToMap] = pressedInput[1] | |
} | |
} | |
if (mappingNow === 'dpad') { | |
const buttonAmount = mapping.dpad.length | |
const buttonToMap = buttonAmount - countNulls(mapping.dpad) | |
if (buttonToMap !== buttonAmount) { | |
const currentButtonDom = dpadDoms.children[buttonToMap] | |
if (buttonToMap !== 0) { | |
removeClass(currentButtonDom.previousElementSibling, 'mapping') | |
} | |
addClass(currentButtonDom, 'mapping') | |
} else { | |
removeClass(dpadDoms.children[buttonToMap - 1], 'mapping') | |
mappingNow = false | |
} | |
if (mappingNow && pressedInput) { | |
mapping.dpad[buttonToMap] = pressedInput[1] | |
} | |
} | |
if (mappingNow === 'button') { | |
const buttonAmount = mapping.buttons.length | |
const buttonToMap = buttonAmount - countNulls(mapping.buttons) | |
if (buttonToMap !== buttonAmount) { | |
const currentButtonDom = buttonDoms.children[buttonToMap] | |
if (buttonToMap !== 0) { | |
removeClass(currentButtonDom.previousElementSibling, 'mapping') | |
} | |
addClass(currentButtonDom, 'mapping') | |
} else { | |
removeClass(buttonDoms.children[buttonToMap - 1], 'mapping') | |
mappingNow = false | |
} | |
if (mappingNow && pressedInput) { | |
mapping.buttons[buttonToMap] = pressedInput[1] | |
} | |
} | |
if (!mappingNow) { | |
const domBeingMapped = document.getElementsByClassName('mapping') | |
for (let i = 0; i < domBeingMapped.length; i++) { | |
removeClass(domBeingMapped[i], 'mapping') | |
} | |
} | |
} else { | |
if (useAnalog) { | |
gpDomSet.analogLeft( | |
gamepads[0].axes[mapping.analogLeft[0]], | |
gamepads[0].axes[mapping.analogLeft[1]] | |
) | |
} else { | |
gpDomSet.dpad( | |
gamepads[0].buttons[mapping.dpad[0]], | |
gamepads[0].buttons[mapping.dpad[1]], | |
gamepads[0].buttons[mapping.dpad[2]], | |
gamepads[0].buttons[mapping.dpad[3]] | |
) | |
} | |
gpDomSet.buttons( | |
gamepads[0].buttons[mapping.buttons[0]], | |
gamepads[0].buttons[mapping.buttons[1]], | |
gamepads[0].buttons[mapping.buttons[2]], | |
gamepads[0].buttons[mapping.buttons[3]], | |
gamepads[0].buttons[mapping.buttons[4]] | |
) | |
} | |
} | |
axisDom.addEventListener('mousedown', function (e) { | |
if (isRightClick(e)) { | |
useAnalog = !useAnalog | |
} else if (!mappingNow) { | |
/* | |
axis dom lies after dpad doms, | |
and I want the user to be able to remap either analog or dpad | |
by clicking representing area. | |
if you click the arrows the className will be 'analogLeft', | |
if you click the circle it will be empty. | |
*/ | |
if (e.target.className === 'analogLeft') { | |
mappingNow = 'dpad' | |
mapping.dpad = [null, null, null, null] | |
} else { | |
mappingNow = 'axis' | |
mapping.analogLeft = [null, null] | |
} | |
} | |
}) | |
buttonDoms.addEventListener('click', function (e) { | |
if (!mappingNow) { | |
mappingNow = 'button' | |
mapping.buttons = [null, null, null, null, null] | |
} | |
}) | |
const popupButton = document.getElementById('openPopup') | |
if (window.name === 'popup') { | |
popupButton.style.display = 'none' | |
} | |
popupButton.addEventListener('click', function () { | |
window.open( | |
window.document.URL, 'popup', | |
`width=${16 * 7}px,height=${16 * 3}px,` + | |
`top=${(screen.availHeight - 39) / 2},` + | |
`left=${(screen.availWidth - 91) / 2}` | |
) | |
}) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> | |
<title>Simple 4-Button Gamepad Input Display</title> | |
<!-- grabGamepadInfo.js: https://gist.github.com/Dinir/75bf0d028cf30f27e131364dc4905cd4 --> | |
<script src="grabGamepadInfo.js"></script> | |
<link rel="stylesheet" href="style.css" /> | |
</head> | |
<body> | |
<div id="gamepad"> | |
<div class="dpad"> | |
<span></span> | |
<span></span> | |
<span></span> | |
<span></span> | |
</div> | |
<div class="analogLeft"> | |
<span></span> | |
</div> | |
<div class="buttons"> | |
<span>γ </span> | |
<span>γ </span> | |
<span>γ </span> | |
<span>γ </span> | |
<span>γ </span> | |
</div> | |
</div> | |
<div id="openPopup"> | |
<span>NEW WINDOW</span> | |
</div> | |
<script src="logic.js"></script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 0; | |
padding: 0; | |
background-color: transparent; | |
font-family: "Fira Code Retina", monospace; | |
font-size: 16px; } | |
span { | |
color: white; | |
display: block; | |
position: absolute; | |
width: 1em; | |
height: 1em; | |
text-align: center; } | |
div#gamepad { | |
width: 7em; | |
height: 3em; } | |
div.analogLeft, | |
div.dpad, | |
div.buttons { | |
height: 3em; | |
position: absolute; } | |
div.analogLeft, | |
div.dpad { | |
width: 3em; | |
top: 0; | |
left: 0; } | |
div.buttons { | |
width: 4em; } | |
div.analogLeft span { | |
background-color: white; | |
border-radius: 100%; | |
top: .75em; | |
left: .75em; | |
width: 1.5em; | |
height: 1.5em; } | |
div.analogLeft.pressed span { | |
background-color: red; } | |
div.dpad span:nth-child(1) { | |
top: -.25em; | |
left: 1em; } | |
div.dpad span:nth-child(2) { | |
top: 2.25em; | |
left: 1em; } | |
div.dpad span:nth-child(3) { | |
top: 1em; | |
left: -.25em; } | |
div.dpad span:nth-child(4) { | |
top: 1em; | |
left: 2.25em; } | |
div.buttons { | |
top: 0; | |
left: 3em; } | |
div.buttons span:nth-child(1), | |
div.buttons span:nth-child(2) { | |
bottom: .5em; } | |
div.buttons span:nth-child(3), | |
div.buttons span:nth-child(4) { | |
top: .5em; } | |
div.buttons span:nth-child(1) { | |
left: 0.25em; } | |
div.buttons span:nth-child(2) { | |
left: 1.25em; } | |
div.buttons span:nth-child(3) { | |
left: 0.75em; } | |
div.buttons span:nth-child(4) { | |
left: 1.75em; } | |
div.buttons span:nth-child(5) { | |
top: 1em; | |
left: 3em; } | |
div.dpad span { | |
color: grey; | |
border: 0.5em solid transparent; | |
width: 0; | |
height: 0; } | |
div.dpad span:nth-child(1) { | |
border-bottom: 0.5em solid grey; } | |
div.dpad span:nth-child(2) { | |
border-top: 0.5em solid grey; } | |
div.dpad span:nth-child(3) { | |
border-right: 0.5em solid grey; } | |
div.dpad span:nth-child(4) { | |
border-left: 0.5em solid grey; } | |
div.buttons span { | |
border-radius: 100%; } | |
div.buttons span.pressed { | |
color: orange; | |
background-color: red; } | |
div.dpad span.mapping:nth-child(1) { | |
border-bottom-color: yellow; } | |
div.dpad span.mapping:nth-child(2) { | |
border-top-color: yellow; } | |
div.dpad span.mapping:nth-child(3) { | |
border-right-color: yellow; } | |
div.dpad span.mapping:nth-child(4) { | |
border-left-color: yellow; } | |
div.analogLeft span.mapping, | |
div.buttons span.mapping { | |
background-color: yellow; | |
color: grey; } | |
div#openPopup { | |
cursor: pointer; | |
position: absolute; | |
top: 1em; | |
left: 8em; | |
width: 6em; } | |
div#openPopup span { | |
width: initial; | |
height: initial; | |
background-color: darkgrey; | |
color: initial; } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 0; | |
padding: 0; | |
background-color: transparent; | |
font-family: "Fira Code Retina", monospace; | |
font-size: 16px; | |
} | |
span { | |
color: white; | |
display: block; | |
position: absolute; | |
width: 1em; | |
height: 1em; | |
text-align: center; | |
} | |
// position | |
div#gamepad { | |
width: 7em; | |
height: 3em; | |
} | |
div.analogLeft, | |
div.dpad, | |
div.buttons | |
{ | |
height: 3em; | |
position: absolute; | |
} | |
div.analogLeft, | |
div.dpad { | |
width: 3em; | |
top: 0; | |
left: 0; | |
} | |
div.buttons { | |
width: 4em; | |
} | |
div.analogLeft { | |
span { | |
background-color: white; | |
border-radius: 100%; | |
top: .75em; | |
left: .75em; | |
width: 1.5em; | |
height: 1.5em; | |
} | |
&.pressed span { | |
background-color: red; | |
} | |
} | |
div.dpad span { | |
&:nth-child(1) { | |
top: -.25em; | |
left: 1em; | |
} | |
&:nth-child(2) { | |
top: 2.25em; | |
left: 1em; | |
} | |
&:nth-child(3) { | |
top: 1em; | |
left: -.25em; | |
} | |
&:nth-child(4) { | |
top: 1em; | |
left: 2.25em; | |
} | |
} | |
div.buttons { | |
top: 0; | |
left: 3em; | |
span:nth-child(1), | |
span:nth-child(2) { | |
bottom: .5em; | |
} | |
span:nth-child(3), | |
span:nth-child(4) { | |
top: .5em; | |
} | |
span:nth-child(1) { left: 0.25em; } | |
span:nth-child(2) { left: 1.25em; } | |
span:nth-child(3) { left: 0.75em; } | |
span:nth-child(4) { left: 1.75em; } | |
span:nth-child(5) { top: 1em; left: 3em; } | |
} | |
// decoration | |
div.dpad span { | |
$dpad-border-area: .5em solid; | |
color: grey; | |
border: $dpad-border-area transparent; | |
width: 0; | |
height: 0; | |
&:nth-child(1) { | |
border-bottom: $dpad-border-area grey; | |
} | |
&:nth-child(2) { | |
border-top: $dpad-border-area grey; | |
} | |
&:nth-child(3) { | |
border-right: $dpad-border-area grey; | |
} | |
&:nth-child(4) { | |
border-left: $dpad-border-area grey; | |
} | |
} | |
div.buttons span { | |
border-radius: 100%; | |
&.pressed { | |
color: orange; | |
background-color: red; | |
} | |
} | |
div.dpad span.mapping { | |
&:nth-child(1) { border-bottom-color: yellow; } | |
&:nth-child(2) { border-top-color: yellow; } | |
&:nth-child(3) { border-right-color: yellow; } | |
&:nth-child(4) { border-left-color: yellow; } | |
} | |
div.analogLeft span.mapping, | |
div.buttons span.mapping { | |
background-color: yellow; | |
color: grey; | |
} | |
// popup button style | |
div#openPopup { | |
cursor: pointer; | |
position: absolute; | |
top: 1em; | |
left: 8em; | |
width: 6em; | |
span { | |
width: initial; | |
height: initial; | |
background-color: darkgrey; | |
color: initial; | |
} | |
} |
Author
Dinir
commented
Aug 7, 2017
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment