various updates and fixes

This commit is contained in:
e7d 2025-03-26 17:45:26 +01:00
parent 7657c70ba6
commit d1d3336913
No known key found for this signature in database
GPG Key ID: F322DD1C685F15D2
11 changed files with 97 additions and 428 deletions

View File

@ -1,7 +1,12 @@
* {
box-sizing: border-box;
}
body {
background: transparent;
font-family: sans-serif;
height: 100vh;
margin: 0;
overflow: hidden;
}
@ -84,13 +89,20 @@ body.unsupported #gamepad {
}
}
#placeholder {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
#placeholder svg {
height: 90%;
left: 50%;
max-width: 600px;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 90%;
}
@ -98,6 +110,10 @@ body.unsupported #gamepad {
animation: press-button 5s linear infinite;
}
#placeholder #placeholder-instructions {
margin-top: -40px;
}
#gamepad {
width: 100vw;
height: 100vh;

349
css/normalize.css vendored
View File

@ -1,349 +0,0 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

View File

@ -1,5 +1,3 @@
version: "3.3"
services:
web:
image: nginx

View File

@ -17,16 +17,12 @@
<meta name="msapplication-TileColor" content="#666666">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="css/normalize.css?v=8.0.1">
<link rel="stylesheet" href="css/gamepad.css">
</head>
<body>
<div id="unsupported">Sorry, but your browser does not support the <a target="_blank"
href="https://developer.mozilla.org/docs/Web/API/Gamepad_API">Gamepad API</a>.</div>
<div id="instructions" style="display: none;">
<p>Press <kbd>H</kbd> to read instructions.</p>
</div>
<div id="placeholder">
<svg version="1.1" viewBox="0 0 549.3125 367.98749" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
<defs>
@ -89,8 +85,12 @@
</g>
</g>
</svg>
<div id="placeholder-instructions">Press and hold any button to start.</div>
</div>
<div id="gamepad"></div>
<div id="instructions" style="display: none;">
<p>Press <kbd>H</kbd> or <a href="#">click here</a> to read instructions.</p>
</div>
<div id="overlay" style="display: none;">
<span id="gamepad-id">
<label for="gamepad-id-select">Gamepad</label>

View File

@ -17,6 +17,7 @@ class Gamepad {
// cached DOM references
this.$body = document.querySelector('body');
this.$instructions = document.querySelector('#instructions');
this.$instructionsLink = this.$instructions.querySelector('a');
this.$placeholder = document.querySelector('#placeholder');
this.$gamepad = document.querySelector('#gamepad');
this.$overlay = document.querySelector('#overlay');
@ -166,7 +167,8 @@ class Gamepad {
// by default, enqueue a delayed display of the placeholder animation
this.displayPlaceholder();
// listen for keyboard events
// listen for click events
this.$instructionsLink.addEventListener('click', this.toggleHelp.bind(this));
this.$helpPopoutClose.addEventListener('click', this.toggleHelp.bind(this));
}
@ -374,7 +376,9 @@ class Gamepad {
* Updates the list of connected gamepads in the overlay
*/
updateGamepadList() {
this.$gamepadSelect.querySelectorAll('.entry').forEach($entry => $entry.remove());
for (const $entry of this.$gamepadSelect.querySelectorAll('.entry')) {
$entry.remove();
}
const $options = [];
for (let key = 0; key < this.gamepads.length; key++) {
const gamepad = this.gamepads[key];
@ -591,7 +595,7 @@ class Gamepad {
}
// else, determine the template to use from the gamepad identifier and update settings
for (let gamepadType in this.identifiers) {
for (const gamepadType in this.identifiers) {
if (this.identifiers[gamepadType].id.test(gamepad.id)) {
return gamepadType;
}
@ -830,7 +834,7 @@ class Gamepad {
// save the buttons mapping of this template
this.mapping.buttons = activeGamepad.buttons.map((_, index) => {
const $button = document.querySelector(`[data-button='${index}']`);
return { $button, button: { pressed: null, value: null } };
return { $button, button: { pressed: null, touched: null, value: null } };
});
// save the axes mapping of this template
@ -856,7 +860,7 @@ class Gamepad {
// destruct and clear the template
if (this.template.destructor) this.template.destructor();
delete this.template;
this.template = undefined;
}
/**
@ -912,7 +916,8 @@ class Gamepad {
}
// save the updated button
this.mapping.buttons[index].button = updatedButton;
const { pressed, touched, value } = updatedButton;
this.mapping.buttons[index].button = { pressed, touched, value };
});
}
@ -926,13 +931,15 @@ class Gamepad {
gamepad.axes.forEach((updatedAxis, index) => {
// get the axis information
const { $axis, attribute, axis } = this.mapping.axes[index];
if (!$axis || updatedAxis === axis) return;
if (!$axis) return;
// update the display value
if (updatedAxis !== axis) {
$axis.setAttribute(attribute.replace('-axis', '-value'), updatedAxis);
// ensure we have an axis updater callback and hook the template defined axis update method
if ('function' === typeof this.updateAxis) this.updateAxis($axis, attribute, updatedAxis);
}
// save the updated button
this.mapping.axes[index].axis = updatedAxis;
@ -1017,13 +1024,13 @@ class Gamepad {
(c) => c === color
);
} else {
if (!isNaN(parseInt(color))) {
if (!Number.isNaN(Number.parseInt(color))) {
// the color is a number, load it by its index
this.colorIndex = color;
} else {
// the color is a string, load it by its name
this.colorIndex = 0;
for (let gamepadColorIndex in this.identifier.colors) {
for (const gamepadColorIndex in this.identifier.colors) {
if (color === this.identifier.colors[gamepadColorIndex]) {
this.colorIndex = gamepadColorIndex;
break;
@ -1074,9 +1081,9 @@ class Gamepad {
} else if (level === '-' && this.zoomLevel > 0.1) {
// '-' means a zoom out if we still can
this.zoomLevel -= 0.1;
} else if (!isNaN((level = parseFloat(level)))) {
// an integer value means a value-based zoom
this.zoomLevel = level;
} else if (!Number.isNaN(+level)) {
// a number value means a value-based zoom
this.zoomLevel = Number.parseFloat(level);
}
// hack: fix js float issues
@ -1169,7 +1176,7 @@ class Gamepad {
* @returns {string|boolean|null}
*/
getUrlParam(name) {
let matches = new RegExp('[?&]' + name + '(=([^&#]*))?').exec(
const matches = new RegExp(`[?&]${name}(=([^&#]*))?`).exec(
window.location.search
);
return matches ? decodeURIComponent(matches[2] || true) || true : null;
@ -1186,10 +1193,10 @@ class Gamepad {
.split('&')
.map((param) => param.split('='));
const settings = {};
Object.keys(settingsArr).forEach((key) => {
for (const key of Object.keys(settingsArr)) {
const [k, v] = settingsArr[key];
settings[k] = v;
});
};
return settings;
}

View File

@ -4,7 +4,7 @@
list-style: none;
display: flex;
flex-flow: row wrap;
max-width: 600px;
width: 600px;
}
.box {
@ -15,22 +15,18 @@
.box.small {
width: 12.5%;
min-width: 50px;
}
.box.medium {
width: 25%;
min-width: 100px;
}
.box.large {
width: 50%;
min-width: 200px;
}
.box.extra-large {
width: 75%;
min-width: 400px;
}
.box .content {

View File

@ -13,8 +13,8 @@ window.gamepad.TemplateClass = class DebugTemplate {
* Destroys the template
*/
destructor() {
delete this.gamepad.updateButton;
delete this.gamepad.updateAxis;
this.gamepad.updateButton = undefined;
this.gamepad.updateAxis = undefined;
}
/**
@ -103,7 +103,7 @@ window.gamepad.TemplateClass = class DebugTemplate {
updateElem($elem, value, precision = 2) {
this.updateTimestamp();
$elem.innerHTML = value.toFixed(precision);
let color = Math.floor(255 * 0.3 + 255 * 0.7 * Math.abs(value));
const color = Math.floor(255 * 0.3 + 255 * 0.7 * Math.abs(value));
$elem.style.setProperty('color', `rgb(${color}, ${color}, ${color})`);
}
@ -115,6 +115,6 @@ window.gamepad.TemplateClass = class DebugTemplate {
if (!this.activeGamepad) {
return;
}
this.$timestamp.innerHTML = parseFloat(this.activeGamepad.timestamp).toFixed(3);
this.$timestamp.innerHTML = Number.parseFloat(this.activeGamepad.timestamp).toFixed(3);
}
};

View File

@ -14,14 +14,14 @@ window.gamepad.TemplateClass = class DualShock4Template {
* Destroys the template
*/
destructor() {
delete this.gamepad.updateButton;
delete this.gamepad.updateAxis;
this.gamepad.updateButton = undefined;
this.gamepad.updateAxis = undefined;
}
updateButton($button, button) {
if (!$button.matches('.trigger') || !button) return;
$button.style.setProperty('opacity', this.gamepad.triggersMeter ? 1 : `${button.value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.triggersMeter ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
$button.style.setProperty('opacity', this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.useMeterTriggers ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
}
updateAxis($axis, attribute, axis) {
@ -37,8 +37,8 @@ window.gamepad.TemplateClass = class DualShock4Template {
}
updateRotate($axis) {
const rotateX = parseFloat($axis.getAttribute('data-value-y') * 30);
const rotateY = -parseFloat($axis.getAttribute('data-value-x') * 30);
const rotateX = Number.parseFloat($axis.getAttribute('data-value-y') * 30);
const rotateY = -Number.parseFloat($axis.getAttribute('data-value-x') * 30);
$axis.style.setProperty('transform', `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`);
}
};

View File

@ -14,14 +14,14 @@ window.gamepad.TemplateClass = class DualSenseTemplate {
* Destroys the template
*/
destructor() {
delete this.gamepad.updateButton;
delete this.gamepad.updateAxis;
this.gamepad.updateButton = undefined;
this.gamepad.updateAxis = undefined;
}
updateButton($button, button) {
if (!$button.matches('.trigger') || !button) return;
$button.style.setProperty('opacity', this.gamepad.triggersMeter ? 1 : `${button.value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.triggersMeter ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
$button.style.setProperty('opacity', this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.useMeterTriggers ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
}
updateAxis($axis, attribute, axis) {
@ -37,8 +37,8 @@ window.gamepad.TemplateClass = class DualSenseTemplate {
}
updateRotate($axis) {
const rotateX = parseFloat($axis.getAttribute('data-value-y') * 30);
const rotateY = -parseFloat($axis.getAttribute('data-value-x') * 30);
const rotateX = Number.parseFloat($axis.getAttribute('data-value-y') * 30);
const rotateY = -Number.parseFloat($axis.getAttribute('data-value-x') * 30);
$axis.style.setProperty('transform', `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`);
}
};

View File

@ -106,14 +106,15 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.withSteering = gamepad.getUrlParam('steeringIndex') !== null;
this.angle = gamepad.getUrlParam('angle') || 360;
this.frequency = gamepad.getUrlParam('fps') || 60;
this.AXES.forEach((axis) => {
for(const axis of this.AXES) {
this[axis] = {
type: (gamepad.getUrlParam(`${axis}Type`) || 'axis').replace('axis', 'axe'),
index: gamepad.getUrlParam(`${axis}Index`),
min: gamepad.getUrlParam(`${axis}Min`) || -1,
max: gamepad.getUrlParam(`${axis}Max`) || 1,
};
}
});
}
/**
@ -254,8 +255,8 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
const { width, height } = this.$chart;
chartContext.clearRect(0, 0, width, height);
this.AXES.forEach((axis) => {
if (axis === 'steering') return;
for (const axis of this.AXES) {
if (axis === 'steering') continue;
chartContext.beginPath();
for (let index = 0; index < this.chartData.length; index++) {
@ -267,7 +268,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
chartContext.strokeStyle = this.chartColors[axis];
chartContext.stroke();
});
}
}
/**
@ -332,7 +333,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.$clutch.style.display = '';
} else {
this.$clutch.style.display = 'none';
this.chartData.forEach((data) => data.clutch = 0);
for (const data of this.chartData) { data.clutch = 0; }
}
});
$inputs.$brakeOption.addEventListener('change', () => {
@ -341,7 +342,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.$brake.style.display = '';
} else {
this.$brake.style.display = 'none';
this.chartData.forEach((data) => data.brake = 0);
for (const data of this.chartData) { data.brake = 0; }
}
});
$inputs.$throttleOption.addEventListener('change', () => {
@ -350,7 +351,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.$throttle.style.display = '';
} else {
this.$throttle.style.display = 'none';
this.chartData.forEach((data) => data.throttle = 0);
for (const data of this.chartData) { data.throttle = 0; }
}
});
$inputs.$steeringOption.addEventListener('change', () => {
@ -371,7 +372,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.setupChart(true);
});
$historyOption.addEventListener('change', () => {
this.historyLength = parseInt($historyOption.value) * 1000;
this.historyLength = Number.parseInt($historyOption.value) * 1000;
this.setupChart(true);
});
$metersOption.addEventListener('change', () => {
@ -387,7 +388,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
this.angle = $steeringAngleOption.value;
});
$fpsOption.addEventListener('change', () => {
this.interval = 1000 / parseInt($fpsOption.value);
this.interval = 1000 / Number.parseInt($fpsOption.value);
console.log(this.interval);
this.setupChart(true);
});
@ -561,15 +562,15 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
await this.waitButtonClick();
return {
...this.AXES.reduce((options, axis) => ({
...options,
[`with${this.capitalize(axis)}`]: $inputs[`$${axis}Option`].checked
}), {}),
...this.AXES.reduce((options, axis) => Object.assign(
options,
{ [`with${this.capitalize(axis)}`]: $inputs[`$${axis}Option`].checked }
), {}),
chart: $chartOption.checked,
history: parseInt($historyOption.value) * 1000,
history: Number.parseInt($historyOption.value) * 1000,
meters: $metersOption.checked,
angle: $steeringAngleOption.value,
fps: parseInt($fpsOption.value)
fps: Number.parseInt($fpsOption.value)
};
}

View File

@ -12,14 +12,14 @@ window.gamepad.TemplateClass = class XboxOneTemplate {
* Destroys the template
*/
destructor() {
delete this.gamepad.updateButton;
delete this.gamepad.updateAxis;
this.gamepad.updateButton = undefined;
this.gamepad.updateAxis = undefined;
}
updateButton($button, button) {
if (!$button.matches('.trigger') || !button) return;
$button.style.setProperty('opacity', this.gamepad.triggersMeter ? 1 : `${button.value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.triggersMeter ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
$button.style.setProperty('opacity', this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.useMeterTriggers ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
}
updateAxis($axis, attribute, axis) {
@ -35,8 +35,8 @@ window.gamepad.TemplateClass = class XboxOneTemplate {
}
updateRotate($axis) {
const rotateX = parseFloat($axis.getAttribute('data-value-y') * 30);
const rotateY = -parseFloat($axis.getAttribute('data-value-x') * 30);
const rotateX = Number.parseFloat($axis.getAttribute('data-value-y') * 30);
const rotateY = -Number.parseFloat($axis.getAttribute('data-value-x') * 30);
$axis.style.setProperty('transform', `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`);
}
};