remove jQuery dependencies

This commit is contained in:
e7d 2023-06-01 18:13:15 +02:00
parent eca6c946f6
commit ea8d03386d
No known key found for this signature in database
GPG Key ID: F320BE007C0B8881
15 changed files with 784 additions and 525 deletions

View File

@ -124,11 +124,6 @@ body.unsupported #gamepad {
padding: 0 8px; padding: 0 8px;
} }
#overlay #color,
#overlay #triggers {
display: none;
}
#overlay select { #overlay select {
padding: 4px 0; padding: 4px 0;
border-radius: 4px; border-radius: 4px;
@ -252,13 +247,11 @@ kbd {
inset 0 -0.05em 0.4em rgb(80, 80, 80), 0 0.1em 0 rgb(30, 30, 30), inset 0 -0.05em 0.4em rgb(80, 80, 80), 0 0.1em 0 rgb(30, 30, 30),
0 0.1em 0.1em rgba(0, 0, 0, 0.3); 0 0.1em 0.1em rgba(0, 0, 0, 0.3);
background: -moz-linear-gradient(top, rgb(60, 60, 60), rgb(80, 80, 80)); background: -moz-linear-gradient(top, rgb(60, 60, 60), rgb(80, 80, 80));
background: -webkit-gradient( background: -webkit-gradient(linear,
linear,
left top, left top,
left bottom, left bottom,
from(rgb(60, 60, 60)), from(rgb(60, 60, 60)),
to(rgb(80, 80, 80)) to(rgb(80, 80, 80)));
);
background: rgb(80, 80, 80); background: rgb(80, 80, 80);
box-shadow: inset 0 0 1px rgb(150, 150, 150), box-shadow: inset 0 0 1px rgb(150, 150, 150),
inset 0 -0.05em 0.4em rgb(80, 80, 80), 0 0.1em 0 rgb(30, 30, 30), inset 0 -0.05em 0.4em rgb(80, 80, 80), 0 0.1em 0 rgb(30, 30, 30),
@ -266,3 +259,33 @@ kbd {
color: rgb(250, 250, 250); color: rgb(250, 250, 250);
text-shadow: -1px -1px 0 rgb(70, 70, 70); text-shadow: -1px -1px 0 rgb(70, 70, 70);
} }
.fadeIn {
animation: fadeIn 300ms;
opacity: 1;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.fadeOut {
animation: fadeOut 1s;
opacity: 0;
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@ -113,13 +113,13 @@
<option value="magenta">Magenta</option> <option value="magenta">Magenta</option>
</select> </select>
</span> </span>
<span id="color"> <span id="color" style="display: none;">
<label for="color">Color</label> <label for="color">Color</label>
<select name="color"> <select name="color">
<option value="">Default</option> <option value="">Default</option>
</select> </select>
</span> </span>
<span id="triggers"> <span id="triggers" style="display: none;">
<label for="triggers">Triggers</label> <label for="triggers">Triggers</label>
<select name="triggers"> <select name="triggers">
<option value="opacity">Opacity</option> <option value="opacity">Opacity</option>
@ -213,7 +213,6 @@
</div> </div>
</div> </div>
<script src="js/jquery.min.js"></script>
<script src="js/gamepad.js"></script> <script src="js/gamepad.js"></script>
</body> </body>

View File

@ -9,21 +9,21 @@ class Gamepad {
*/ */
constructor() { constructor() {
// cached DOM references // cached DOM references
this.$body = $('body'); this.$body = document.querySelector('body');
this.$instructions = $('#instructions'); this.$instructions = document.querySelector('#instructions');
this.$placeholder = $('#placeholder'); this.$placeholder = document.querySelector('#placeholder');
this.$gamepad = $('#gamepad'); this.$gamepad = document.querySelector('#gamepad');
this.$overlay = $('#overlay'); this.$overlay = document.querySelector('#overlay');
this.$gamepadSelect = $('select[name=gamepad-id]'); this.$gamepadSelect = document.querySelector('select[name=gamepad-id]');
this.$skinSelect = $('select[name=skin]'); this.$skinSelect = document.querySelector('select[name=skin]');
this.$backgroundSelect = $('select[name=background]'); this.$backgroundSelect = document.querySelector('select[name=background]');
this.$colorOverlay = this.$overlay.find('#color'); this.$colorOverlay = this.$overlay.querySelector('#color');
this.$colorSelect = this.$colorOverlay.find('select[name=color]'); this.$colorSelect = this.$colorOverlay.querySelector('select[name=color]');
this.$triggersOverlay = this.$overlay.find('#triggers'); this.$triggersOverlay = this.$overlay.querySelector('#triggers');
this.$triggersSelect = this.$triggersOverlay.find('select[name=triggers]'); this.$triggersSelect = this.$triggersOverlay.querySelector('select[name=triggers]');
this.$helpPopout = $('#help.popout'); this.$helpPopout = document.querySelector('#help.popout');
this.$helpPopoutClose = this.$helpPopout.find('.close'); this.$helpPopoutClose = this.$helpPopout.querySelector('.close');
this.$gamepadList = $('#gamepad-list'); this.$gamepadList = document.querySelector('#gamepad-list');
// ensure the GamePad API is available on this browser // ensure the GamePad API is available on this browser
this.assertGamepadAPI(); this.assertGamepadAPI();
@ -172,16 +172,16 @@ class Gamepad {
break; break;
} }
} }
if (backgroundStyleIndex) {
if (backgroundStyleIndex)
this.changeBackgroundStyle(backgroundStyleIndex); this.changeBackgroundStyle(backgroundStyleIndex);
} }
}
// by default, enqueue a delayed display of the placeholder animation // by default, enqueue a delayed display of the placeholder animation
this.displayPlaceholder(); this.displayPlaceholder();
// listen for keyboard events // listen for keyboard events
this.$helpPopoutClose.on('click', this.toggleHelp.bind(this)); this.$helpPopoutClose.addEventListener('click', this.toggleHelp.bind(this));
} }
/** /**
@ -194,7 +194,7 @@ class Gamepad {
? () => navigator.webkitGetGamepads() ? () => navigator.webkitGetGamepads()
: null; : null;
if (!getGamepadsFn) { if (!getGamepadsFn) {
this.$body.addClass('unsupported'); this.$body.classList.add('unsupported');
throw new Error('Unsupported gamepad API'); throw new Error('Unsupported gamepad API');
} }
this.getNavigatorGamepads = getGamepadsFn; this.getNavigatorGamepads = getGamepadsFn;
@ -204,23 +204,65 @@ class Gamepad {
* Initialises the overlay selectors * Initialises the overlay selectors
*/ */
initOverlaySelectors() { initOverlaySelectors() {
this.$gamepadSelect.on('change', () => this.$gamepadSelect.addEventListener('change', () =>
this.changeGamepad(this.$gamepadSelect.val()) this.changeGamepad(this.$gamepadSelect.value)
); );
this.$skinSelect.on('change', () => this.$skinSelect.addEventListener('change', () =>
this.changeSkin(this.$skinSelect.val()) this.changeSkin(this.$skinSelect.value)
); );
this.$backgroundSelect.on('change', () => this.$backgroundSelect.addEventListener('change', () =>
this.changeBackgroundStyle(this.$backgroundSelect.val()) this.changeBackgroundStyle(this.$backgroundSelect.value)
); );
this.$colorSelect.on('change', () => this.$colorSelect.addEventListener('change', () =>
this.changeGamepadColor(this.$colorSelect.val()) this.changeGamepadColor(this.$colorSelect.value)
); );
this.$triggersSelect.on('change', () => this.$triggersSelect.addEventListener('change', () =>
this.toggleTriggersMeter(this.$triggersSelect.val() === 'meter') this.toggleTriggersMeter(this.$triggersSelect.value === 'meter')
); );
} }
/**
* Shows an HTML element
*
* @param {HTMLElement} $element
*/
show($element) {
$element.style.removeProperty('display');
$element.classList.remove('fadeIn', 'fadeOut');
}
/**
* Hides an HTML element
*
* @param {HTMLElement} $element
*/
hide($element) {
$element.style.setProperty('display', 'none');
$element.classList.remove('fadeIn', 'fadeOut');
}
/**
* Fades in an HTML element
*
* @param {HTMLElement} $element
*/
fadeIn($element) {
$element.style.removeProperty('display');
$element.classList.remove('fadeOut');
$element.classList.add('fadeIn');
}
/**
* Fades out an HTML element
*
* @param {HTMLElement} $element
*/
fadeOut($element) {
$element.style.removeProperty('display');
$element.classList.remove('fadeIn');
$element.classList.add('fadeOut');
}
/** /**
* Displays the instructions * Displays the instructions
*/ */
@ -228,10 +270,8 @@ class Gamepad {
// do not display help if we have an active gamepad // do not display help if we have an active gamepad
if (null !== this.index) return; if (null !== this.index) return;
// cancel the queued display of the instructions animation, if any
window.clearTimeout(this.instructionsTimeout);
// show the instructions // show the instructions
this.$instructions.show(); this.fadeIn(this.$instructions);
// enqueue a delayed display of the instructions animation // enqueue a delayed display of the instructions animation
this.hideInstructions(); this.hideInstructions();
@ -243,15 +283,20 @@ class Gamepad {
* @param {boolean} [hideNow=false] * @param {boolean} [hideNow=false]
*/ */
hideInstructions(hideNow = false) { hideInstructions(hideNow = false) {
// cancel the queued display of the instructions animation, if any
window.clearTimeout(this.instructionsTimeout);
// hide the message right away if needed // hide the message right away if needed
if (hideNow) { if (hideNow) {
this.$instructions.hide(); this.hide(this.$instructions);
return;
} }
// hide instructions animation if no gamepad is active after X ms // hide instructions animation if no gamepad is active after X ms
this.instructionsTimeout = window.setTimeout(() => { this.instructionsTimeout = window.setTimeout(
this.$instructions.fadeOut(); () => this.fadeOut(this.$instructions),
}, this.instructionsDelay); this.instructionsDelay
);
} }
/** /**
@ -261,10 +306,8 @@ class Gamepad {
// do not display help if we have an active gamepad // do not display help if we have an active gamepad
if (null !== this.index) return; if (null !== this.index) return;
// cancel the queued display of the placeholder animation, if any
window.clearTimeout(this.placeholderTimeout);
// show the placeholder // show the placeholder
this.$placeholder.show(); this.fadeIn(this.$placeholder);
// enqueue a delayed display of the placeholder animation // enqueue a delayed display of the placeholder animation
this.hidePlaceholder(); this.hidePlaceholder();
@ -276,25 +319,28 @@ class Gamepad {
* @param {boolean} [hideNow=false] * @param {boolean} [hideNow=false]
*/ */
hidePlaceholder(hideNow = false) { hidePlaceholder(hideNow = false) {
// cancel the queued display of the placeholder animation, if any
window.clearTimeout(this.placeholderTimeout);
// hide the animation right away if needed // hide the animation right away if needed
if (hideNow) { if (hideNow) {
this.$placeholder.hide(); this.hide(this.$placeholder);
return;
} }
// hide placeholder animation if no gamepad is active after X ms // hide placeholder animation if no gamepad is active after X ms
this.placeholderTimeout = window.setTimeout(() => { this.placeholderTimeout = window.setTimeout(
this.$placeholder.fadeOut(); () => this.fadeOut(this.$placeholder),
}, this.placeholderDelay); this.placeholderDelay
);
} }
/** /**
* Displays the overlay animation on screen * Displays the overlay animation on screen
*/ */
displayOverlay() { displayOverlay() {
// cancel the queued display of the overlay animation, if any
window.clearTimeout(this.overlayTimeout);
// show the overlay // show the overlay
this.$overlay.show(); this.fadeIn(this.$overlay);
// enqueue a delayed display of the overlay animation // enqueue a delayed display of the overlay animation
this.hideOverlay(); this.hideOverlay();
@ -306,15 +352,20 @@ class Gamepad {
* @param {boolean} [hideNow=false] * @param {boolean} [hideNow=false]
*/ */
hideOverlay(hideNow = false) { hideOverlay(hideNow = false) {
// cancel the queued display of the overlay animation, if any
window.clearTimeout(this.overlayTimeout);
// hide the message right away if needed // hide the message right away if needed
if (hideNow) { if (hideNow) {
this.$overlay.hide(); this.hide(this.$overlay);
return;
} }
// hide overlay animation if no gamepad is active after X ms // hide overlay animation if no gamepad is active after X ms
this.overlayTimeout = window.setTimeout(() => { this.overlayTimeout = window.setTimeout(
this.$overlay.fadeOut(); () => this.fadeOut(this.$overlay),
}, this.overlayDelay); this.overlayDelay
);
} }
/** /**
@ -331,19 +382,17 @@ class Gamepad {
* Updates the list of connected gamepads in the overlay * Updates the list of connected gamepads in the overlay
*/ */
updateGamepadList() { updateGamepadList() {
this.$gamepadSelect.find('.entry').remove(); this.$gamepadSelect.querySelectorAll('.entry').forEach($entry => $entry.remove());
const $options = []; const $options = [];
for (let key = 0; key < this.gamepads.length; key++) { for (let key = 0; key < this.gamepads.length; key++) {
const gamepad = this.gamepads[key]; const gamepad = this.gamepads[key];
if (!gamepad) { if (!gamepad) continue;
continue;
}
const { name } = this.toGamepadInfo(gamepad.id); const { name } = this.toGamepadInfo(gamepad.id);
$options.push( $options.push(
`<option class='entry' value='${gamepad.id}'>${name}</option>` `<option class='entry' value='${gamepad.id}'>${name}</option>`
); );
} }
this.$gamepadSelect.append($options.join('')); this.$gamepadSelect.innerHTML += $options.join('');
} }
/** /**
@ -351,21 +400,21 @@ class Gamepad {
*/ */
updateColors() { updateColors() {
if (!this.type) { if (!this.type) {
this.$colorOverlay.hide(); this.hide(this.$colorOverlay);
return; return;
} }
const colors = this.identifiers[this.type].colors; const colors = this.identifiers[this.type].colors;
if (!colors) { if (!colors) {
this.$colorOverlay.hide(); this.hide(this.$colorOverlay);
return; return;
} }
const colorOptions = colors.map( const colorOptions = colors.map(
(color) => `<option value='${color}'>${color.charAt(0).toUpperCase()}${color.slice(1)}</option>` (color) => `<option value='${color}'>${color.charAt(0).toUpperCase()}${color.slice(1)}</option>`
); );
this.$colorSelect.html(colorOptions); this.$colorSelect.innerHTML = colorOptions.join('');
this.$colorOverlay.fadeIn(); this.show(this.$colorOverlay);
} }
/** /**
@ -373,17 +422,17 @@ class Gamepad {
*/ */
updateTriggers() { updateTriggers() {
if (!this.type) { if (!this.type) {
this.$triggersOverlay.hide(); this.hide(this.$triggersOverlay);
return; return;
} }
const triggers = this.identifiers[this.type].triggers; const triggers = this.identifiers[this.type].triggers;
if (!triggers) { if (!triggers) {
this.$triggersOverlay.hide(); this.hide(this.$triggersOverlay);
return; return;
} }
this.$triggersOverlay.fadeIn(); this.show(this.$triggersOverlay);
} }
/** /**
@ -416,7 +465,7 @@ class Gamepad {
if (e.gamepad.index === this.index) { if (e.gamepad.index === this.index) {
// display a disconnection indicator // display a disconnection indicator
this.$gamepad.addClass('disconnected'); this.$gamepad.classList.add('disconnected');
this.disconnectedIndex = e.gamepad.index; this.disconnectedIndex = e.gamepad.index;
// refresh gamepad list on help, if displayed // refresh gamepad list on help, if displayed
@ -500,6 +549,9 @@ class Gamepad {
// get fresh information from DOM about gamepads // get fresh information from DOM about gamepads
const gamepads = this.getNavigatorGamepads(); const gamepads = this.getNavigatorGamepads();
if (gamepads !== this.gamepads) this.gamepads = gamepads; if (gamepads !== this.gamepads) this.gamepads = gamepads;
// when visible, refresh gamepad list with latest data
if (this.helpVisible) this.buildHelpGamepadList();
} }
/** /**
@ -509,19 +561,16 @@ class Gamepad {
const $tbody = []; const $tbody = [];
for (let key = 0; key < this.gamepads.length; key++) { for (let key = 0; key < this.gamepads.length; key++) {
const gamepad = this.gamepads[key]; const gamepad = this.gamepads[key];
if (!gamepad) { if (!gamepad) continue;
continue; $tbody.push(`<tr><td>${gamepad.index}</td><td>${gamepad.id}</td></tr>`);
} }
$tbody.push(
`<tr><td>${gamepad.index}</td><td>${gamepad.id}</td></tr>'`
);
}
if ($tbody.length === 0) { if ($tbody.length === 0) {
$tbody.push('<tr><td colspan="2">No gamepad detected.</td></tr>'); this.$gamepadList.innerHTML = '<tr><td colspan="2">No gamepad detected.</td></tr>';
return;
} }
this.$gamepadList.html($tbody.join('')); this.$gamepadList.innerHTML = $tbody.join('');
} }
/** /**
@ -583,21 +632,17 @@ class Gamepad {
if ( if (
null !== this.disconnectedIndex && null !== this.disconnectedIndex &&
index !== this.disconnectedIndex index !== this.disconnectedIndex
) ) continue;
continue;
const gamepad = this.gamepads[index]; const gamepad = this.gamepads[index];
if (!gamepad) continue; if (!gamepad) continue;
// check the parameters for a selected gamepad // check the parameters for a selected gamepad
const gamepadId = this.getUrlParam('gamepad'); const gamepadId = this.getUrlParam('gamepad');
if (gamepadId) { if (gamepadId === gamepad.id) {
const [vendor, product] = gamepadId.split('-');
if (gamepad.id.includes(vendor) && gamepad.id.includes(product)) {
this.map(gamepad.index); this.map(gamepad.index);
return; return;
} }
}
// read the gamepad buttons // read the gamepad buttons
let button; let button;
@ -642,13 +687,12 @@ class Gamepad {
// hide the help messages // hide the help messages
this.hideInstructions(true); this.hideInstructions(true);
this.$helpPopout.removeClass('active');
this.hidePlaceholder(true); this.hidePlaceholder(true);
// update local references // update local references
this.index = index; this.index = index;
this.disconnectedIndex = null; this.disconnectedIndex = null;
this.$gamepad.removeClass('disconnected'); this.$gamepad.classList.remove('disconnected');
const gamepad = this.getActive(); const gamepad = this.getActive();
// ensure that a gamepad was actually found for this index // ensure that a gamepad was actually found for this index
@ -670,16 +714,12 @@ class Gamepad {
this.identifier = this.identifiers[this.type]; this.identifier = this.identifiers[this.type];
// update the overlay selectors // update the overlay selectors
this.$gamepadSelect.val(`${gamepad.id}`); this.$gamepadSelect.value = gamepad.id;
this.updateColors(); this.updateColors();
this.updateTriggers(); this.updateTriggers();
// load the HTML template file // load the HTML template file
this.loadTemplate(gamepad); this.loadTemplate();
// hide the help before displaying the template
this.hideInstructions();
this.hidePlaceholder();
} }
/** /**
@ -704,6 +744,9 @@ class Gamepad {
// ensure we have something to disconnect // ensure we have something to disconnect
if (this.index === null) return; if (this.index === null) return;
// clear the current template
this.clearTemplate();
// clear associated data // clear associated data
this.index = null; this.index = null;
this.disconnectedIndex = null; this.disconnectedIndex = null;
@ -714,25 +757,49 @@ class Gamepad {
this.colorIndex = null; this.colorIndex = null;
this.colorName = null; this.colorName = null;
this.zoomLevel = 1; this.zoomLevel = 1;
this.$gamepad.empty(); this.$gamepad.innerHTML = '';
this.$gamepadSelect.val('auto') this.$gamepad.classList.remove('fadeIn');
this.$gamepadSelect.value = 'auto';
this.updateColors(); this.updateColors();
this.updateTriggers(); this.updateTriggers();
this.clearUrlParams(); this.clearUrlParams();
} }
/** /**
* Load the HTML template file for the active gamepad * Loads the template script and stylesheet
*
* @param {*} gamepad
*/ */
loadTemplate(gamepad) { loadTemplateAssets() {
// hide the gamepad while we prepare it const script = document.createElement('script');
this.$gamepad.hide(); script.async = true;
script.src = `templates/${this.type}/template.js`;
script.onload = () => {
// initialize the template
new this.template();
$.ajax(`templates/${this.type}/template.html`).done((template) => { // enqueue the initial display refresh
this.startTemplate();
}
this.$gamepad.appendChild(script);
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = `templates/${this.type}/template.css`;
this.$gamepad.appendChild(link);
}
/**
* Load the HTML template file for the active gamepad
*/
loadTemplate() {
// hide the gamepad while we prepare it
this.$gamepad.style.setProperty('display', 'none');
fetch(`templates/${this.type}/template.html`)
.then((response) => response.text())
.then((template) => {
// inject the template HTML // inject the template HTML
this.$gamepad.html(template); this.$gamepad.innerHTML = template;
this.loadTemplateAssets();
// read for parameters to apply: // read for parameters to apply:
const identifier = this.identifiers[this.type]; const identifier = this.identifiers[this.type];
@ -761,26 +828,52 @@ class Gamepad {
this.updateUrlParams({ zoom: undefined }); this.updateUrlParams({ zoom: undefined });
} }
// once fully loaded, display the gamepad
this.$gamepad.style.removeProperty('display');
this.$gamepad.classList.remove('fadeOut');
this.$gamepad.classList.add('fadeIn');
});
}
/**
* Starts the template
*/
startTemplate() {
// get the active gamepad
const activeGamepad = this.getActive();
// save the buttons mapping of this template // save the buttons mapping of this template
this.mapping.buttons = []; this.mapping.buttons = [];
for (let index = 0; index < gamepad.buttons.length; index++) { for (let index = 0; index < activeGamepad.buttons.length; index++) {
this.mapping.buttons[index] = $(`[data-button='${index}']`); const $button = document.querySelector(`[data-button='${index}']`);
if (!$button) break;
this.mapping.buttons[index] = $button;
} }
// save the axes mapping of this template // save the axes mapping of this template
this.mapping.axes = []; this.mapping.axes = [];
for (let index = 0; index < gamepad.axes.length; index++) { for (let index = 0; index < activeGamepad.axes.length; index++) {
this.mapping.axes[index] = $( const $axis = document.querySelector(
`[data-axis=${index}], [data-axis-x=${index}], [data-axis-y=${index}], [data-axis-z=${index}]` `[data-axis='${index}'], [data-axis-x='${index}'], [data-axis-y='${index}'], [data-axis-z='${index}']`
); );
if (!$axis) break;
this.mapping.axes[index] = $axis;
} }
// enqueue the initial display refresh // enqueue the initial display refresh
this.pollStatus(true); this.pollStatus(true);
}
// once fully loaded, display the gamepad /**
this.$gamepad.fadeIn(); * Clears the template
}); */
clearTemplate() {
// ensure that a tempalte is currently loaded
if (!this.template) return;
// destruct and clear the template
if (this.template.destructor) this.template.destructor();
delete this.template;
} }
/** /**
@ -815,6 +908,9 @@ class Gamepad {
* @param {*} gamepad * @param {*} gamepad
*/ */
updateButtons(gamepad) { updateButtons(gamepad) {
// ensure we have a button updater callback
if ('function' !== typeof this.updateButton) return;
// update the buttons // update the buttons
for (let index = 0; index < gamepad.buttons.length; index++) { for (let index = 0; index < gamepad.buttons.length; index++) {
// find the DOM element // find the DOM element
@ -828,15 +924,13 @@ class Gamepad {
const button = gamepad.buttons[index]; const button = gamepad.buttons[index];
// update the display values // update the display values
$button.attr('data-pressed', button.pressed); $button.setAttribute('data-pressed', button.pressed);
$button.attr('data-value', button.value); $button.setAttribute('data-value', button.value);
// hook the template defined button update method // hook the template defined button update method
if ('function' === typeof this.updateButton) {
this.updateButton($button); this.updateButton($button);
} }
} }
}
/** /**
* Updates the axes status of the active gamepad * Updates the axes status of the active gamepad
@ -844,6 +938,9 @@ class Gamepad {
* @param {*} gamepad * @param {*} gamepad
*/ */
updateAxes(gamepad) { updateAxes(gamepad) {
// ensure we have an axis updater callback
if ('function' !== typeof this.updateAxis) return;
// update the axes // update the axes
for (let index = 0; index < gamepad.axes.length; index++) { for (let index = 0; index < gamepad.axes.length; index++) {
// find the DOM element // find the DOM element
@ -857,25 +954,23 @@ class Gamepad {
const axis = gamepad.axes[index]; const axis = gamepad.axes[index];
// update the display values // update the display values
if ($axis.is('[data-axis=' + index + ']')) { if ($axis.matches(`[data-axis='${index}']`)) {
$axis.attr('data-value', axis); $axis.setAttribute('data-value', axis);
} }
if ($axis.is('[data-axis-x=' + index + ']')) { if ($axis.matches(`[data-axis-x='${index}']`)) {
$axis.attr('data-value-x', axis); $axis.setAttribute('data-value-x', axis);
} }
if ($axis.is('[data-axis-y=' + index + ']')) { if ($axis.matches(`[data-axis-y='${index}']`)) {
$axis.attr('data-value-y', axis); $axis.setAttribute('data-value-y', axis);
} }
if ($axis.is('[data-axis-z=' + index + ']')) { if ($axis.matches(`[data-axis-z='${index}']`)) {
$axis.attr('data-value-z', axis); $axis.setAttribute('data-value-z', axis);
} }
// hook the template defined axis update method // hook the template defined axis update method
if ('function' === typeof this.updateAxis) {
this.updateAxis($axis); this.updateAxis($axis);
} }
} }
}
/** /**
* Changes the active gamepad * Changes the active gamepad
@ -884,11 +979,7 @@ class Gamepad {
*/ */
changeGamepad(gamepadId) { changeGamepad(gamepadId) {
// get the index corresponding to the identifier of the gamepad // get the index corresponding to the identifier of the gamepad
const index = this.gamepads.findIndex(g => { const index = this.gamepads.findIndex(g => g && gamepadId === g.id);
if (!g) return false;
const { vendor, product } = this.toGamepadInfo(g.id);
return `${vendor}-${product}` === gamepadId;
});
// set the selected gamepad // set the selected gamepad
this.updateUrlParams({ gamepad: gamepadId !== 'auto' ? gamepadId : undefined }); this.updateUrlParams({ gamepad: gamepadId !== 'auto' ? gamepadId : undefined });
@ -901,8 +992,11 @@ class Gamepad {
* @param {any} skin * @param {any} skin
*/ */
changeSkin(skin) { changeSkin(skin) {
// clear the current template
this.clearTemplate();
// update the visual skin selector // update the visual skin selector
this.$skinSelect.val(skin); this.$skinSelect.value = skin;
// set the selected skin // set the selected skin
this.debug = skin === 'debug'; this.debug = skin === 'debug';
@ -931,17 +1025,17 @@ class Gamepad {
this.backgroundStyleName = this.backgroundStyleName =
this.backgroundStyle[this.backgroundStyleIndex]; this.backgroundStyle[this.backgroundStyleIndex];
this.$body.css({ this.$body.style.setProperty(
background: 'background',
this.backgroundStyleName === 'checkered' this.backgroundStyleName === 'checkered'
? 'url(css/transparent-bg.png)' ? 'url(css/transparent-bg.png)'
: this.backgroundStyleName, : this.backgroundStyleName
color: this.textColors[this.backgroundStyleIndex], );
}); this.$body.style.setProperty('color', this.textColors[this.backgroundStyleIndex]);
// update current settings // update current settings
this.updateUrlParams({ background: this.backgroundStyleName }); this.updateUrlParams({ background: this.backgroundStyleName });
this.$backgroundSelect.val(this.backgroundStyleName); this.$backgroundSelect.value = this.backgroundStyleName;
} }
/** /**
@ -983,11 +1077,11 @@ class Gamepad {
: null; : null;
// update the DOM with the color value // update the DOM with the color value
this.$gamepad.attr('data-color', this.colorName); this.$gamepad.setAttribute('data-color', this.colorName);
// update current settings // update current settings
this.updateUrlParams({ color: this.colorName }); this.updateUrlParams({ color: this.colorName });
this.$colorSelect.val(this.colorName); this.$colorSelect.value = this.colorName;
} }
/** /**
@ -1006,9 +1100,10 @@ class Gamepad {
if (this.zoomMode === 'auto') { if (this.zoomMode === 'auto') {
// 'auto' means a 'contained in window' zoom, with a max zoom of 1 // 'auto' means a 'contained in window' zoom, with a max zoom of 1
const { width, height } = this.$gamepad.getBoundingClientRect();
this.zoomLevel = Math.min( this.zoomLevel = Math.min(
window.innerWidth / this.$gamepad.width(), window.innerWidth / width,
window.innerHeight / this.$gamepad.height(), window.innerHeight / height,
1 1
); );
} else if (level === 0) { } else if (level === 0) {
@ -1029,7 +1124,7 @@ class Gamepad {
this.zoomLevel = +this.zoomLevel.toFixed(2); this.zoomLevel = +this.zoomLevel.toFixed(2);
// update the DOM with the zoom value // update the DOM with the zoom value
this.$gamepad.css( this.$gamepad.style.setProperty(
'transform', 'transform',
`translate(-50%, -50%) scale(${this.zoomLevel}, ${this.zoomLevel})` `translate(-50%, -50%) scale(${this.zoomLevel}, ${this.zoomLevel})`
); );
@ -1084,13 +1179,9 @@ class Gamepad {
* Toggles the on-screen help message * Toggles the on-screen help message
*/ */
toggleHelp() { toggleHelp() {
// refresh gamepad list with latest data
this.pollGamepads();
this.buildHelpGamepadList();
// display the help popout // display the help popout
this.$helpPopout.toggleClass('active'); this.$helpPopout.classList.toggle('active');
this.helpVisible = this.$helpPopout.is('.active'); this.helpVisible = this.$helpPopout.classList.contains('active');
} }
/** /**
@ -1102,14 +1193,14 @@ class Gamepad {
this.triggersMeter = this.triggersMeter =
useMeter !== undefined ? useMeter : !this.triggersMeter; useMeter !== undefined ? useMeter : !this.triggersMeter;
this.$gamepad[this.triggersMeter ? 'addClass' : 'removeClass']( this.$gamepad.classList[this.triggersMeter ? 'add' : 'remove'](
'triggers-meter' 'triggers-meter'
); );
// update current settings // update current settings
const triggers = this.triggersMeter ? 'meter' : 'opacity'; const triggers = this.triggersMeter ? 'meter' : 'opacity';
this.updateUrlParams({ triggers }); this.updateUrlParams({ triggers });
this.$triggersSelect.val(triggers); this.$triggersSelect.value = triggers;
} }
/** /**

2
js/jquery.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,3 @@
<link rel="stylesheet" href="templates/debug/template.css">
<script async src="templates/debug/template.js"></script>
<div class="info"> <div class="info">
<div class="container"> <div class="container">
<div id="info-name" class="box extra-large"> <div id="info-name" class="box extra-large">

View File

@ -1,88 +1,121 @@
function DebugTemplate(gamepad) { window.gamepad.template = class DebugTemplate {
return { /**
$name: $('#info-name .value'), * Instanciates a new debug template
$vendor: $('#info-vendor'), */
$product: $('#info-product'), constructor() {
$id: $('#info-id'), this.gamepad = window.gamepad;
$timestamp: $('#info-timestamp .value'), this.init();
$index: $('#info-index .value'), this.gamepad.updateButton = ($button) => this.updateElem($button);
$mapping: $('#info-mapping .value'), this.gamepad.updateAxis = ($axis) => this.updateElem($axis, 6);
$rumble: $('#info-rumble .value'),
$axes: $('.axes .container'),
$buttons: $('.buttons .container'),
activeGamepad: gamepad.getActive(),
init: function () {
if (!this.activeGamepad) {
return;
} }
const { name, vendor, product, id } = gamepad.toGamepadInfo(this.activeGamepad.id);
this.$name.html(name).attr('title', name); /**
* Destroys the template
*/
destructor() {
delete this.gamepad.updateButton;
delete this.gamepad.updateAxis;
}
/**
* Initializes the template
*/
init() {
this.$name = document.querySelector('#info-name .value');
this.$vendor = document.querySelector('#info-vendor');
this.$product = document.querySelector('#info-product');
this.$id = document.querySelector('#info-id');
this.$timestamp = document.querySelector('#info-timestamp .value');
this.$index = document.querySelector('#info-index .value');
this.$mapping = document.querySelector('#info-mapping .value');
this.$rumble = document.querySelector('#info-rumble .value');
this.$axes = document.querySelector('.axes .container');
this.$buttons = document.querySelector('.buttons .container');
const activeGamepad = this.gamepad.getActive();
const { name, vendor, product, id } = this.gamepad.toGamepadInfo(activeGamepad.id);
this.$name.innerHTML = name;
this.$name.setAttribute('title', activeGamepad.id);
if (vendor && product) { if (vendor && product) {
this.$vendor.css({display: 'block'}).find('.value').html(vendor); this.$vendor.querySelector('.value').innerHTML = vendor;
this.$product.css({display: 'block'}).find('.value').html(product); this.$product.querySelector('.value').innerHTML = product;
this.$vendor.style.setProperty('display', 'block');
this.$product.style.setProperty('display', 'block');
} else { } else {
this.$id.css({display: 'block'}).find('.value').html(id); this.$id.querySelector('.value').innerHTML = id;
this.$id.style.setProperty('display', 'block');
} }
this.updateTimestamp(); this.updateTimestamp();
this.$index.html(this.activeGamepad.index); this.$index.innerHTML = this.activeGamepad.index;
this.$mapping.html(this.activeGamepad.mapping || 'N/A'); this.$mapping.innerHTML = this.activeGamepad.mapping || 'N/A';
this.$rumble.html( this.$rumble.innerHTML = this.activeGamepad.vibrationActuator
this.activeGamepad.vibrationActuator
? this.activeGamepad.vibrationActuator.type ? this.activeGamepad.vibrationActuator.type
: 'N/A' : 'N/A';
);
this.initAxes(); this.initAxes();
this.initButtons(); this.initButtons();
gamepad.updateButton = ($button) => this.updateElem($button); }
gamepad.updateAxis = ($axis) => this.updateElem($axis, 6);
}, /**
initAxes: function () { * Initializes the axes
*/
initAxes() {
for ( for (
let axisIndex = 0; let axisIndex = 0;
axisIndex < this.activeGamepad.axes.length; axisIndex < this.activeGamepad.axes.length;
axisIndex++ axisIndex++
) { ) {
this.$axes.append(` this.$axes.innerHTML += `
<div class="box medium"> <div class="box medium">
<div class="content"> <div class="content">
<div class="label">Axis ${axisIndex}</div> <div class="label">Axis ${axisIndex}</div>
<div class="value" data-axis="${axisIndex}"></div> <div class="value" data-axis="${axisIndex}"></div>
</div> </div>
</div> </div>
`); `;
} }
}, }
initButtons: function () {
/**
* Initializes the buttons
*/
initButtons() {
for ( for (
let buttonIndex = 0; let buttonIndex = 0;
buttonIndex < this.activeGamepad.buttons.length; buttonIndex < this.activeGamepad.buttons.length;
buttonIndex++ buttonIndex++
) { ) {
this.$buttons.append(` this.$buttons.innerHTML += `
<div class="box small"> <div class="box small">
<div class="content"> <div class="content">
<div class="label">B${buttonIndex}</div> <div class="label">B${buttonIndex}</div>
<div class="value" data-button="${buttonIndex}"></div> <div class="value" data-button="${buttonIndex}"></div>
</div> </div>
</div> </div>
`); `;
} }
}, }
updateElem: function ($elem, precision = 2) {
/**
* Updates the value of an element
*
* @param {Element} $elem
* @param {Number} precision
*/
updateElem($elem, precision = 2) {
this.updateTimestamp(); this.updateTimestamp();
let value = parseFloat($elem.attr('data-value'), 10).toFixed(precision); let value = parseFloat($elem.attributes['data-value'].value, 10).toFixed(precision);
$elem.html(value); $elem.innerHTML = value;
let color = Math.floor(255 * 0.3 + 255 * 0.7 * Math.abs(value)); let color = Math.floor(255 * 0.3 + 255 * 0.7 * Math.abs(value));
$elem.css({ color: `rgb(${color}, ${color}, ${color})` }); $elem.style.setProperty('color', `rgb(${color}, ${color}, ${color})`);
}, }
updateTimestamp: function () {
this.activeGamepad = gamepad.getActive(); /**
* Updates the timestamp
*/
updateTimestamp() {
this.activeGamepad = this.gamepad.getActive();
if (!this.activeGamepad) { if (!this.activeGamepad) {
return; return;
} }
this.$timestamp.html(parseFloat(this.activeGamepad.timestamp).toFixed(3)); this.$timestamp.innerHTML = parseFloat(this.activeGamepad.timestamp).toFixed(3);
}, }
}.init();
}; };
new DebugTemplate(window.gamepad);

View File

@ -1,5 +1,3 @@
<link rel="stylesheet" href="templates/ds4/template.css">
<script async src="templates/ds4/template.js"></script>
<div class="triggers"> <div class="triggers">
<span class="trigger left" data-button="6"></span> <span class="trigger left" data-button="6"></span>
<span class="trigger right" data-button="7"></span> <span class="trigger right" data-button="7"></span>

View File

@ -1,37 +1,34 @@
function DualShock4Template(gamepad) { window.gamepad.template = class DualShock4Template {
return { /**
init: function () { * Instanciates a new DualShock 4 controller template
gamepad.updateButton = function ($button) { */
const value = parseFloat($button.attr('data-value'), 10); constructor() {
if ($button.is('.trigger')) { this.gamepad = window.gamepad;
$button.css( this.gamepad.updateButton = ($button) => this.updateButton($button);
gamepad.triggersMeter this.gamepad.updateAxis = ($axis) => this.updateAxis($axis);
? {
opacity: 1,
'clip-path': `inset(${(1 - value) * 100}% 0px 0px 0pc)`,
} }
: {
opacity: `${value * 100}%`, /**
'clip-path': 'none', * Destroys the template
*/
destructor() {
delete this.gamepad.updateButton;
delete this.gamepad.updateAxis;
} }
);
updateButton($button) {
if (!$button.matches('.trigger')) return;
const value = parseFloat($button.getAttribute('data-value'), 10);
$button.style.setProperty('opacity', this.gamepad.triggersMeter ? 1 : `${value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.triggersMeter ? `inset(${100 - value * 100}% 0px 0px 0pc)` : 'none');
}
updateAxis($axis) {
if (!$axis.matches('.stick')) return;
const axisX = $axis.getAttribute('data-value-x');
const axisY = $axis.getAttribute('data-value-y');
$axis.style.setProperty('margin-top', `${axisY * 25}px`);
$axis.style.setProperty('margin-left', `${axisX * 25}px`);
$axis.style.setProperty('transform', `rotateX(${-parseFloat(axisY * 30, 8)}deg) rotateY(${parseFloat(axisX * 30, 8)}deg)`);
} }
}; };
gamepad.updateAxis = function ($axis) {
const axisX = $axis.attr('data-value-x');
const axisY = $axis.attr('data-value-y');
if ($axis.is('.stick')) {
$axis.css({
'margin-top': axisY * 25,
'margin-left': axisX * 25,
transform: `rotateX(${-parseFloat(
axisY * 30,
8
)}deg) rotateY(${parseFloat(axisX * 30, 8)}deg)`,
});
}
};
},
}.init();
};
new DualShock4Template(window.gamepad);

View File

@ -0,0 +1,45 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 698">
<path fill="#e6e6e6"
d="M344.3 131c-9 1.9-21.5 6.9-32.1 12.9-11.5 6.4-15.8 10.3-16.8 14.9-.3 1.5-.7 3.7-1 4.8-.3 1.6-1.6 2.5-4.8 3.3-4.2 1-4.8 1.6-10.6 10.9-8.3 13.3-21.7 40.6-28.9 58.7-23.5 59.6-36.7 124.8-43.3 213.5-1.7 22.5-1.7 106.2 0 130 .7 9.6 1.5 21.4 1.8 26.2.5 7.6 1 9.4 4.2 14.8 9.3 15.8 28.1 29.1 45.7 32.6 3.8.7 11.9 2.4 17.9 3.8 5.9 1.4 13.8 2.8 17.5 3.2 7.4.7 9.2-.3 10.5-6.1 1.4-5.5 9.6-27.2 17.5-46.2 18.1-43.5 31.9-72.5 44.3-93.5 10.8-18.4 26.7-33.8 40.1-39.1 6.5-2.5 18.2-2.6 48.7-.3 32.9 2.4 265.1 2.3 288.5-.2 23.8-2.5 36.1-2.2 45.9 1.2 4.3 1.5 9.8 4 12.1 5.6 5.7 3.9 21.5 22 27.7 31.6 12 18.8 27 53.5 48.4 112.4 7.6 20.7 10.5 27 14.3 30.4 1.8 1.7 3.5 1.8 18.8 1.4 16.5-.4 17-.5 28.5-4.5 16.8-5.8 28.2-13.2 37.6-24.7 8.2-9.9 9-12.4 10.2-30.6 4.3-67.2 3.6-131.6-2-185.5-6.9-67-20.4-126-40.1-176-7.2-18.1-20.6-45.4-28.9-58.7-5-8.1-6.6-9.9-9.3-10.6-2.8-.7-3.3-1.4-4.3-5.8-2.2-9.2-2.9-9.9-19.5-18.8-11.9-6.3-21.3-9.9-30.9-11.6-7.4-1.4-48.3-.4-51.3 1.2-2.9 1.5-5.7 7-5.7 11v3.7l-18.2-.5c-22.7-.6-28.6-1-30.1-2.2-2.8-2.3-47.9-6.4-90-8.2-29.7-1.3-119.7-1.3-132.9 0-5.7.6-15.2 1.5-21.3 2.1-6 .6-14.4 1.4-18.5 1.9s-12.9 1.4-19.5 2c-6.6.7-13.7 1.8-15.7 2.6-2.7 1-10 1.4-25 1.6l-21.3.3-.1-3.7c-.1-4.1-2.3-8-5.7-10.2-2.7-1.7-45.6-3-52.4-1.6z" />
<path fill="#1a1a1a"
d="M398.1 121.37q-48.86-8.04-97.8.15 4.84-24.2 11.45-45.52 1.69-5.44 4.72-12.11 8.27-18.18 26.78-24.64 6.77-2.36 13.97-1.62 4.95.51 9.9 3.4 5.03 2.94 10.19 7.16 4.69 3.83 7.39 7.65 8.11 11.47 10.05 25.41 1.42 10.19 3.35 40.12Zm-35.24-13.34q.86-1.03 3.08-2.59 3.49-2.46 2.17-5.66-1.05-2.52-3.89-3.14-4.14-.91-5.58 3.88a.68.65-65.1 0 0 .26.77q1.91 1.22 2.79-1.1 1.02-2.7 2.75-1.13 1.51 1.38.95 2.95-.29.8-1.82 1.97-2.76 2.11-4.58 5.21a1.19 1.19 15.7 0 0 1.01 1.8q3.56.05 6.72-.03.92-.03 1.37-.34.82-.58.34-1.62a.87.76-9.9 0 0-.75-.45l-4.62-.1q-.54-.01-.2-.42Zm-17.7-10.34-.12 12.18a1.16 1.12.2 0 0 1.16 1.13h7.03q1.03 0 1.5-.25.96-.52.43-1.74a.81.76-9.9 0 0-.72-.47l-5.73-.04a.71.69 0 0 1-.71-.69V97.75a.98.84-76.7 0 0-.41-.8q-.99-.61-1.98-.05a.95.88 76.8 0 0-.45.79Zm550.82 23.71q-19.2-2.44-38.55-3.43-10.39-.53-19.99-.2-13.21.46-22.25 1.79-8.43 1.25-16.9 2.19 1.39-22.42 3.21-38.5 3.02-26.67 26.06-40.94 11.55-7.15 24.21-2.98 18.59 6.12 27.15 22.2 2.69 5.06 6.93 18.72 6.19 19.96 10.13 41.15Zm-64.44-15.49q5.49-1.61 5.14 4.09-.12 1.91 2.69 1.39a.46.43-9.1 0 0 .37-.48l-.48-4.35a1.66.98 32.1 0 0-.44-.85q-1.14-1.24.17-2.04a1.52.96-16.5 0 0 .61-.7q1.76-5.6-4.34-5.87-3.38-.15-6.62-.1a.65.65-.4 0 0-.64.65q0 6.23-.22 12.09-.08 2.35 2.6 1.66a.86.84 83.1 0 0 .63-.83l.03-4.01a.7.67-8.7 0 1 .5-.65Zm16.38 2.42q1.33-1.31 2.73-2.55 1.77-1.57 2.32-2.85 1.32-3.1-1.28-4.88-1.93-1.33-4.23-1.08-3.32.37-3.62 4.21a.73.67-78 0 0 .43.72q1.38.53 2.04-.54.61-1 .64-1.03 2.33-2.71 3.41.69a1.37 1.32-42.4 0 1-.06.95q-1.09 2.45-3.41 3.96-2.5 1.62-3.32 5a.46.46 6.9 0 0 .45.57h8.48a1.01.82 13.2 0 0 .8-.39q.65-1-.02-1.92a1.16.91-13.6 0 0-.85-.39l-4.37-.1q-.5-.01-.14-.37Z" />
<path fill="#2c2c2c"
d="m363.06 108.45 4.62.1a.87.76-9.9 0 1 .75.45q.48 1.04-.34 1.62-.45.31-1.37.34-3.16.08-6.72.03a1.19 1.19 15.7 0 1-1.01-1.8q1.82-3.1 4.58-5.21 1.53-1.17 1.82-1.97.56-1.57-.95-2.95-1.73-1.57-2.75 1.13-.88 2.32-2.79 1.1a.68.65-65.1 0 1-.26-.77q1.44-4.79 5.58-3.88 2.84.62 3.89 3.14 1.32 3.2-2.17 5.66-2.22 1.56-3.08 2.59-.34.41.2.42ZM345.61 96.9q.99-.56 1.98.05a.98.84-76.7 0 1 .41.8v10.06a.71.69 0 0 0 .71.69l5.73.04a.81.76-9.9 0 1 .72.47q.53 1.22-.43 1.74-.47.25-1.5.25h-7.03a1.16 1.12.2 0 1-1.16-1.13l.12-12.18a.95.88 76.8 0 1 .45-.79Zm485.43 9.66-.03 4.01a.86.84 83.1 0 1-.63.83q-2.68.69-2.6-1.66.22-5.86.22-12.09a.65.65-.4 0 1 .64-.65q3.24-.05 6.62.1 6.1.27 4.34 5.87a1.52.96-16.5 0 1-.61.7q-1.31.8-.17 2.04a1.66.98 32.1 0 1 .44.85l.48 4.35a.46.43-9.1 0 1-.37.48q-2.81.52-2.69-1.39.35-5.7-5.14-4.09a.7.67-8.7 0 0-.5.65Zm5.87-5.66a1.39 1.39 0 0 0-1.39-1.39h-3.2a1.39 1.39 0 0 0-1.39 1.39v.96a1.39 1.39 0 0 0 1.39 1.39h3.2a1.39 1.39 0 0 0 1.39-1.39v-.96Zm11.15 7.8 4.37.1a1.16.91-13.6 0 1 .85.39q.67.92.02 1.92a1.01.82 13.2 0 1-.8.39h-8.48a.46.46 6.9 0 1-.45-.57q.82-3.38 3.32-5 2.32-1.51 3.41-3.96a1.37 1.32-42.4 0 0 .06-.95q-1.08-3.4-3.41-.69-.03.03-.64 1.03-.66 1.07-2.04.54a.73.67-78 0 1-.43-.72q.3-3.84 3.62-4.21 2.3-.25 4.23 1.08 2.6 1.78 1.28 4.88-.55 1.28-2.32 2.85-1.4 1.24-2.73 2.55-.36.36.14.37Z" />
<path fill="#1a1a1a"
d="M836.91 101.86a1.39 1.39 0 0 1-1.39 1.39h-3.2a1.39 1.39 0 0 1-1.39-1.39v-.96a1.39 1.39 0 0 1 1.39-1.39h3.2a1.39 1.39 0 0 1 1.39 1.39v.96Z" />
<path
d="m398.1 121.37.69 6.17a.91.89-.7 0 1-.98.98q-11.67-.92-23.05-1.63-21.63-1.34-43.26-.07-15.89.94-32.42 2.49a.4.38 2.9 0 1-.44-.46l1.66-7.33q48.94-8.19 97.8-.15Zm497.88.03 1.75 7.45a.62.58-3.2 0 1-.67.7q-17.06-1.51-33.82-2.5-10.59-.63-19.98-.5-22.23.32-44.48 2.4a.94.87-.7 0 1-1.03-.93l.54-6.27q8.47-.94 16.9-2.19 9.04-1.33 22.25-1.79 9.6-.33 19.99.2 19.35.99 38.55 3.43Z" />
<path fill="#232323"
d="M392.5 133.29q3.22.48.34 2.08-2 1.11-4.24 1.53-1.98.38-2.06.4-22.73 5.27-45.52 10.27-14.4 3.17-35.52 5.93-2.81.37-2.79-2.25 6.94-4.32 16.98-9.56 14.1-7.37 29.33-9.13 6.07-.71 19.97-.16 11.76.46 23.51.89Zm503.86 18.79q-2.56 1.3-4.11 1.17-15.22-1.24-30.11-4.55-10.19-2.26-10.32-2.28-14.26-3.01-28.48-6.25-1.34-.31-10.21-2.19-7.28-1.54-10.07-2.79-2.03-.91.01-1.86 10.31-.47 20.43-.8 17.43-.55 20-.43 15.44.7 29.56 7.34 11.3 5.31 23.3 12.64Z" />
<path fill="#1a1a1a"
d="M392.5 133.29q3.3-.13 4.69 1.02 6.61 5.5 5.31 14.4-54.64 6.44-108.2 18.95.44-5.01 1.2-7.66 1.64-5.72 7.21-8.75-.02 2.62 2.79 2.25 21.12-2.76 35.52-5.93 22.79-5 45.52-10.27.08-.02 2.06-.4 2.24-.42 4.24-1.53 2.88-1.6-.34-2.08Zm410.57.04q-2.04.95-.01 1.86 2.79 1.25 10.07 2.79 8.87 1.88 10.21 2.19 14.22 3.24 28.48 6.25.13.02 10.32 2.28 14.89 3.31 30.11 4.55 1.55.13 4.11-1.17 7.16 6.06 6.58 15.91-43.18-9.46-74.94-14.74-11.88-1.97-32.86-4.05-2.23-11.53 7.93-15.87Z" />
<path fill="#0170bb"
d="M749.8 147.21q3.75-.57 6.54.71 5.97 6.72 8.85 13.14 2.51 5.62 1.56 10.69-4.85 25.85-14 64.75-2.02 8.57-4.79 22.75-4.05 3.49-8.21.21 8.39-41.35 17.19-82.6.55-2.57.57-5.28.02-2.67.07-5.33.23-11.29-7.78-19.04Zm-304.63-.3q-9.38 9.43-8.21 21.84.49 5.14 1.65 10.17l1.2 5.2q7.4 34.93 14.58 69.88-.08 4.76-4.22 4.82-1.19.01-2.09-.78-.89-.79-1.54-1.79-8.35-36.97-16.19-74.06-2.2-10.35-2.2-10.38-.59-5.68 1.66-10.75 2.74-6.15 8.69-13.02l6.67-1.13Z" />
<path fill="#818081"
d="M795.929 162.798a.54.54 0 0 1-.546.535l-10.06-.106a.54.54 0 0 1-.534-.545l.002-.18a.54.54 0 0 1 .546-.535l10.06.106a.54.54 0 0 1 .534.545l-.002.18Zm-393.287 8.551a.67.67 0 0 1-.408.855l-.057.02a.67.67 0 0 1-.855-.408l-2.884-8.144a.67.67 0 0 1 .408-.856l.057-.02a.67.67 0 0 1 .855.408l2.884 8.144Zm2.994-.513a.56.56 0 0 1-.686.396l-.077-.02a.56.56 0 0 1-.396-.686l1.507-5.622a.56.56 0 0 1 .686-.396l.077.02a.56.56 0 0 1 .396.686l-1.507 5.622Zm390.088-4.843a.27.27 0 0 1-.275.264l-10.158-.195a.27.27 0 0 1-.265-.275l.01-.5a.27.27 0 0 1 .275-.264l10.158.195a.27.27 0 0 1 .265.275l-.01.5Zm.256 2.917a.35.35 0 0 1-.351.35l-10.54-.02a.35.35 0 0 1-.35-.35l.001-.54a.35.35 0 0 1 .351-.35l10.54.02a.35.35 0 0 1 .35.35l-.001.54Zm-396.328 4.24a3.68.72 37.7 0 1-3.352-1.68 3.68.72 37.7 0 1-2.472-2.82 3.68.72 37.7 0 1 3.352 1.68 3.68.72 37.7 0 1 2.472 2.82Zm22.601 31.13a9.73 9.73 0 0 1-7.231 11.707l-.915.216a9.73 9.73 0 0 1-11.707-7.23l-4.873-20.613a9.73 9.73 0 0 1 7.231-11.707l.915-.216a9.73 9.73 0 0 1 11.707 7.23l4.873 20.613Zm-6.34-20.202a8.29 8.29 0 0 0-9.964-6.178l-.74.173a8.29 8.29 0 0 0-6.178 9.964l4.796 20.445a8.29 8.29 0 0 0 9.964 6.178l.74-.173a8.29 8.29 0 0 0 6.178-9.964l-4.796-20.445Z" />
<path fill="#777677"
d="M793.067 208.91a9.67 9.67 0 0 1-11.648 7.166l-.895-.213a9.67 9.67 0 0 1-7.165-11.648l4.894-20.545a9.67 9.67 0 0 1 11.648-7.166l.895.213a9.67 9.67 0 0 1 7.165 11.648l-4.894 20.545Zm3.469-20.904a8.39 8.39 0 0 0-6.253-10.084l-.7-.164a8.39 8.39 0 0 0-10.085 6.252l-4.814 20.524a8.39 8.39 0 0 0 6.253 10.084l.7.164a8.39 8.39 0 0 0 10.085-6.252l4.814-20.524Z" />
<path fill="#d8d8d8"
d="M791.722 208.53a8.39 8.39 0 0 1-10.084 6.252l-.701-.164a8.39 8.39 0 0 1-6.253-10.084l4.814-20.524a8.39 8.39 0 0 1 10.084-6.252l.701.164a8.39 8.39 0 0 1 6.253 10.084l-4.814 20.524Zm-371.013-4.007a8.29 8.29 0 0 1-6.178 9.964l-.74.173a8.29 8.29 0 0 1-9.964-6.178l-4.796-20.445a8.29 8.29 0 0 1 6.178-9.964l.74-.173a8.29 8.29 0 0 1 9.964 6.178l4.796 20.445Z" />
<path fill="#2e3940"
d="M454.39 254q2.5 16.22 8.23 31.48-1.37-.24-2.37.82a1.18 1.13 19.1 0 1-.81.37l-8 .32-1.15-6.48a1.24.49 86.1 0 0-.4-.84q-.26-.16-.6-.17l-2.75-23.25q.65 1 1.54 1.79.9.79 2.09.78 4.14-.06 4.22-4.82Z" />
<path fill="#2e3940"
d="M747.96 259.25q-2.22 14.17-2.25 28.5-.98-.3-1.27.69-1.1 3.68-3.99 7.72-10.12 14.17-25.51 22.28-6.5 3.42-14.18 4.06-.15.01-14.53-.01-90.23-.14-180.46.22-12.52.05-17.45-1.15-8.65-2.11-16.01-7.87-13.81-10.82-20.87-26.7l8-.32a1.18 1.13 19.1 0 0 .81-.37q1-1.06 2.37-.82 7.01 13.56 16.94 21.96 2.9 2.45 7.09 3.7 2.87.86 7.8.86h206.27q.87 0 1.69-.29 13.13-4.7 22.55-14.13 4.46-4.46 7.14-9.41 2.93-5.42 4.09-11.23 2.94-14.77 3.56-17.48 4.16 3.28 8.21-.21Z" />
<path fill="#1e1e1e"
d="M449.29 279.5q.34.01.6.17a1.24.49 86.1 0 1 .4.84l1.15 6.48q7.06 15.88 20.87 26.7 7.36 5.76 16.01 7.87 4.93 1.2 17.45 1.15 90.23-.36 180.46-.22 14.38.02 14.53.01 7.68-.64 14.18-4.06 15.39-8.11 25.51-22.28 2.89-4.04 3.99-7.72.29-.99 1.27-.69.42 8.74 3.85 14.19 5.57 8.82 15.85 21.55 9.42 11.66 9.46 11.72 15.37 19.36 23.44 29.98 2.87 3.77 6.54 9.34 2.68 4.06 5.92 9.73 7.37 12.89 14.73 25.78 18.82 33.63 35.29 68.46 15.42 35.19 29.21 71.04 12.53 32.24 22.89 65.24 3.67 11.69 8.19 23.07 3.17 7.97 8.23 13.84 2.71 3.13 7.49 3.62-21.62 6.52-44.2 3.67-7.23-17.6-9.6-23.98-18.09-48.79-39.87-96.03-2.39-7.8-7.3-14.31-3-5.59-9.52-15.72-.81-1.26-1.35-.44-10.1-12.16-18.52-20.94-3.42-3.56-8.6-6.05-19.16-9.22-40.34-5.76-5.88.96-23 2.25-9.35.7-31.99.59-16.22-.09-20.02-.09h-178.9l-7.67.03q-27.57.11-58.42-2.78-9.45-.88-20.43-.35-6.39.31-10.87 2.07-6.99 2.76-13.51 7.97-14 11.16-22.73 24.81-.91.03-1.41.8-6.18 9.61-11.38 19.79-22.14 43.37-40.23 88.6-9.77 24.42-14.78 41.51-2.74 1.37-5.66 1.05-16.86-1.85-36.25-7.21 4.13-.89 6.81-4.73 3.94-5.63 6-10.5 4.84-11.43 8.59-23.26 11.35-35.82 25.06-70.8 14.55-38.67 31.79-76.21 14.9-30.25 31.25-59.75 7.72-13.32 15.31-26.73 3.34-5.89 7.93-12.49 3.04-4.38 8.9-11.82 31.49-39.99 38.3-48.81 1.38-1.79 4.06-5.96 2.19-3.39 3.13-6.45 2.67-8.6 1.91-17.78Zm181.98 57.13a3.78 3.78 0 0 0-3.78-3.78 3.78 3.78 0 0 0-3.78 3.78 3.78 3.78 0 0 0 3.78 3.78 3.78 3.78 0 0 0 3.78-3.78Zm-58.81 0a3.78 3.78 0 0 0-3.78-3.78 3.78 3.78 0 0 0-3.78 3.78 3.78 3.78 0 0 0 3.78 3.78 3.78 3.78 0 0 0 3.78-3.78Zm14.36.02a3.77 3.77 0 0 0-3.77-3.77 3.77 3.77 0 0 0-3.77 3.77 3.77 3.77 0 0 0 3.77 3.77 3.77 3.77 0 0 0 3.77-3.77Zm15.07.01a3.79 3.79 0 0 0-3.79-3.79 3.79 3.79 0 0 0-3.79 3.79 3.79 3.79 0 0 0 3.79 3.79 3.79 3.79 0 0 0 3.79-3.79Zm14.32 0a3.78 3.78 0 0 0-3.78-3.78 3.78 3.78 0 0 0-3.78 3.78 3.78 3.78 0 0 0 3.78 3.78 3.78 3.78 0 0 0 3.78-3.78Zm-95.26 58.02a52.16 52.16 0 0 0-52.16-52.16 52.16 52.16 0 0 0-52.16 52.16 52.16 52.16 0 0 0 52.16 52.16 52.16 52.16 0 0 0 52.16-52.16Zm258.63-.01a52.17 52.17 0 0 0-52.17-52.17 52.17 52.17 0 0 0-52.17 52.17 52.17 52.17 0 0 0 52.17 52.17 52.17 52.17 0 0 0 52.17-52.17Zm-201.35-45.13a3.11 3.11 0 0 0-3.11-3.11 3.11 3.11 0 0 0-3.11 3.11 3.11 3.11 0 0 0 3.11 3.11 3.11 3.11 0 0 0 3.11-3.11Zm15.13 0a3.12 3.12 0 0 0-3.12-3.12 3.12 3.12 0 0 0-3.12 3.12 3.12 3.12 0 0 0 3.12 3.12 3.12 3.12 0 0 0 3.12-3.12Zm14.36-.01a3.12 3.12 0 0 0-3.12-3.12 3.12 3.12 0 0 0-3.12 3.12 3.12 3.12 0 0 0 3.12 3.12 3.12 3.12 0 0 0 3.12-3.12Zm15.13-.03a3.16 3.16 0 0 0-3.16-3.16 3.16 3.16 0 0 0-3.16 3.16 3.16 3.16 0 0 0 3.16 3.16 3.16 3.16 0 0 0 3.16-3.16Zm-6.14 86.89a3.62 3.62 0 0 0-3.62-3.62h-30.5a3.62 3.62 0 0 0-3.62 3.62v.5a3.62 3.62 0 0 0 3.62 3.62h30.5a3.62 3.62 0 0 0 3.62-3.62v-.5Z" />
<path fill="#070707"
d="M631.27 336.63a3.78 3.78 0 0 1-3.78 3.78 3.78 3.78 0 0 1-3.78-3.78 3.78 3.78 0 0 1 3.78-3.78 3.78 3.78 0 0 1 3.78 3.78Zm-58.81 0a3.78 3.78 0 0 1-3.78 3.78 3.78 3.78 0 0 1-3.78-3.78 3.78 3.78 0 0 1 3.78-3.78 3.78 3.78 0 0 1 3.78 3.78Zm14.36.02a3.77 3.77 0 0 1-3.77 3.77 3.77 3.77 0 0 1-3.77-3.77 3.77 3.77 0 0 1 3.77-3.77 3.77 3.77 0 0 1 3.77 3.77Zm15.07.01a3.79 3.79 0 0 1-3.79 3.79 3.79 3.79 0 0 1-3.79-3.79 3.79 3.79 0 0 1 3.79-3.79 3.79 3.79 0 0 1 3.79 3.79Zm14.32 0a3.78 3.78 0 0 1-3.78 3.78 3.78 3.78 0 0 1-3.78-3.78 3.78 3.78 0 0 1 3.78-3.78 3.78 3.78 0 0 1 3.78 3.78Z" />
<path fill="#0c0c0c"
d="M520.95 394.68a52.16 52.16 0 0 1-52.16 52.16 52.16 52.16 0 0 1-52.16-52.16 52.16 52.16 0 0 1 52.16-52.16 52.16 52.16 0 0 1 52.16 52.16Zm-8.3.05a43.89 43.89 0 0 0-43.89-43.89 43.89 43.89 0 0 0-43.89 43.89 43.89 43.89 0 0 0 43.89 43.89 43.89 43.89 0 0 0 43.89-43.89Zm266.93-.06a52.17 52.17 0 0 1-52.17 52.17 52.17 52.17 0 0 1-52.17-52.17 52.17 52.17 0 0 1 52.17-52.17 52.17 52.17 0 0 1 52.17 52.17Zm-8.35.03a43.85 43.85 0 0 0-43.85-43.85 43.85 43.85 0 0 0-43.85 43.85 43.85 43.85 0 0 0 43.85 43.85 43.85 43.85 0 0 0 43.85-43.85Z" />
<path fill="#070707"
d="M578.23 349.54a3.11 3.11 0 0 1-3.11 3.11 3.11 3.11 0 0 1-3.11-3.11 3.11 3.11 0 0 1 3.11-3.11 3.11 3.11 0 0 1 3.11 3.11Zm15.13 0a3.12 3.12 0 0 1-3.12 3.12 3.12 3.12 0 0 1-3.12-3.12 3.12 3.12 0 0 1 3.12-3.12 3.12 3.12 0 0 1 3.12 3.12Zm14.36-.01a3.12 3.12 0 0 1-3.12 3.12 3.12 3.12 0 0 1-3.12-3.12 3.12 3.12 0 0 1 3.12-3.12 3.12 3.12 0 0 1 3.12 3.12Zm15.13-.03a3.16 3.16 0 0 1-3.16 3.16 3.16 3.16 0 0 1-3.16-3.16 3.16 3.16 0 0 1 3.16-3.16 3.16 3.16 0 0 1 3.16 3.16Z" />
<path fill="#161616"
d="M512.65 394.73a43.89 43.89 0 0 1-43.89 43.89 43.89 43.89 0 0 1-43.89-43.89 43.89 43.89 0 0 1 43.89-43.89 43.89 43.89 0 0 1 43.89 43.89Zm-10.12-.09a33.76 33.76 0 0 0-33.76-33.76 33.76 33.76 0 0 0-33.76 33.76 33.76 33.76 0 0 0 33.76 33.76 33.76 33.76 0 0 0 33.76-33.76Zm268.7.06a43.85 43.85 0 0 1-43.85 43.85 43.85 43.85 0 0 1-43.85-43.85 43.85 43.85 0 0 1 43.85-43.85 43.85 43.85 0 0 1 43.85 43.85Zm-10.07-.07a33.75 33.75 0 0 0-33.75-33.75 33.75 33.75 0 0 0-33.75 33.75 33.75 33.75 0 0 0 33.75 33.75 33.75 33.75 0 0 0 33.75-33.75Z" />
<path fill="#1e1e1e"
d="M502.53 394.64a33.76 33.76 0 0 1-33.76 33.76 33.76 33.76 0 0 1-33.76-33.76 33.76 33.76 0 0 1 33.76-33.76 33.76 33.76 0 0 1 33.76 33.76Zm258.63-.01a33.75 33.75 0 0 1-33.75 33.75 33.75 33.75 0 0 1-33.75-33.75 33.75 33.75 0 0 1 33.75-33.75 33.75 33.75 0 0 1 33.75 33.75Z" />
<path fill="#070707"
d="M616.71 436.89a3.62 3.62 0 0 1-3.62 3.62h-30.5a3.62 3.62 0 0 1-3.62-3.62v-.5a3.62 3.62 0 0 1 3.62-3.62h30.5a3.62 3.62 0 0 1 3.62 3.62v.5Z" />
<path fill="#090806" fill-opacity=".514" d="m843.13 538.97-7.3-14.31q4.91 6.51 7.3 14.31Z" />
<path fill="#e6e6e6"
d="M369.96 510.25q-18.67 53.58-41.01 105.73-7 16.35-15.5 31.96-1.05 1.94-6.26 9.5-1.99 2.88-5.03 3.51 5.01-17.09 14.78-41.51 18.09-45.23 40.23-88.6 5.2-10.18 11.38-19.79.5-.77 1.41-.8ZM835.83 524.66l7.3 14.31Q864.91 586.21 883 635q2.37 6.38 9.6 23.98-5.34-3.66-8.6-9.48-12.23-21.85-21.04-43.02-20.19-48.53-38-97.98.54-.82 1.35.44 6.52 10.13 9.52 15.72Z" />
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,5 +1,3 @@
<link rel="stylesheet" href="templates/dualsense/template.css">
<script async src="templates/dualsense/template.js"></script>
<div class="triggers"> <div class="triggers">
<span class="trigger left" data-button="6"></span> <span class="trigger left" data-button="6"></span>
<span class="trigger right" data-button="7"></span> <span class="trigger right" data-button="7"></span>

View File

@ -1,37 +1,34 @@
function DualShock4Template(gamepad) { window.gamepad.template = class DualSenseTemplate {
return { /**
init: function () { * Instanciates a new DualSense controller template
gamepad.updateButton = function ($button) { */
const value = parseFloat($button.attr('data-value'), 10); constructor() {
if ($button.is('.trigger')) { this.gamepad = window.gamepad;
$button.css( this.gamepad.updateButton = ($button) => this.updateButton($button);
gamepad.triggersMeter this.gamepad.updateAxis = ($axis) => this.updateAxis($axis);
? {
opacity: 1,
'clip-path': `inset(${(1 - value) * 100}% 0px 0px 0pc)`,
} }
: {
opacity: `${value * 100}%`, /**
'clip-path': 'none', * Destroys the template
*/
destructor() {
delete this.gamepad.updateButton;
delete this.gamepad.updateAxis;
} }
);
updateButton($button) {
if (!$button.matches('.trigger')) return;
const value = parseFloat($button.getAttribute('data-value'), 10);
$button.style.setProperty('opacity', this.gamepad.triggersMeter ? 1 : `${value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.triggersMeter ? `inset(${100 - value * 100}% 0px 0px 0pc)` : 'none');
}
updateAxis($axis) {
if (!$axis.matches('.stick')) return;
const axisX = $axis.getAttribute('data-value-x');
const axisY = $axis.getAttribute('data-value-y');
$axis.style.setProperty('margin-top', `${axisY * 25}px`);
$axis.style.setProperty('margin-left', `${axisX * 25}px`);
$axis.style.setProperty('transform', `rotateX(${-parseFloat(axisY * 30, 8)}deg) rotateY(${parseFloat(axisX * 30, 8)}deg)`);
} }
}; };
gamepad.updateAxis = function ($axis) {
const axisX = $axis.attr('data-value-x');
const axisY = $axis.attr('data-value-y');
if ($axis.is('.stick')) {
$axis.css({
'margin-top': axisY * 25,
'margin-left': axisX * 25,
transform: `rotateX(${-parseFloat(
axisY * 30,
8
)}deg) rotateY(${parseFloat(axisX * 30, 8)}deg)`,
});
}
};
},
}.init();
};
new DualShock4Template(window.gamepad);

View File

@ -1,6 +1,4 @@
<link rel="stylesheet" href="templates/telemetry/template.css"> <div id="fps"></div>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="templates/telemetry/template.js"></script>
<div id="telemetry"> <div id="telemetry">
<div id="chart"></div> <div id="chart"></div>
<div id="meters"> <div id="meters">

View File

@ -1,37 +1,63 @@
function TelemetryTemplate(gamepad) { window.gamepad.template = class TelemetryTemplate {
return { /**
$clutchBar: document.querySelector('#clutch .bar'), * Instanciates a new telemetry template
$clutchValue: document.querySelector('#clutch .value'), */
$brakeBar: document.querySelector('#brake .bar'), constructor() {
$brakeValue: document.querySelector('#brake .value'), this.gamepad = window.gamepad;
$throttleBar: document.querySelector('#throttle .bar'), this.init();
$throttleValue: document.querySelector('#throttle .value'), }
$directionIndicator: document.querySelector('#direction .indicator'),
AXES: ['clutch', 'brake', 'throttle', 'direction'], /**
frequency: gamepad.getUrlParam('fps') || 60, * Destroys the template
historyLength: gamepad.getUrlParam('history') || 5000, */
index: 0, destructor() {
init: function () { this.running = false;
this.interval = 1000 / this.frequency; }
this.length = this.historyLength / this.interval;
this.loadAxes(); /**
this.initChart(); * Converts a value to a percentage
}, *
toPercentage: function (value, min, max) { * @param {Number} value
* @param {Number} min
* @param {Number} max
* @returns {Number}
*/
toPercentage(value, min, max) {
return value !== undefined return value !== undefined
? Math.round((value - min) * (100 / (max - min))) ? Math.round((value - min) * (100 / (max - min)))
: 0; : 0;
}, }
toDegrees: function (value, min, max) {
/**
* Converts a value to degrees
*
* @param {Number} value
* @param {Number} min
* @param {Number} max
* @returns {Number}
*/
toDegrees(value, min, max) {
const percentage = this.toPercentage(value, min, max); const percentage = this.toPercentage(value, min, max);
return (this.directionDegrees) * (percentage - 50) / 100; return (this.directionDegrees) * (percentage - 50) / 100;
}, }
toAxisValue: function (gamepad, axis) {
/**
* Set the value of an axis
*
* @param {object} gamepad
* @param {string} axis
* @returns {Number}
*/
toAxisValue(gamepad, axis) {
const { [`${axis}Type`]: type, [`${axis}Index`]: index, [`${axis}Min`]: min, [`${axis}Max`]: max } = this[axis]; const { [`${axis}Type`]: type, [`${axis}Index`]: index, [`${axis}Min`]: min, [`${axis}Max`]: max } = this[axis];
const value = type === 'button' ? gamepad.buttons[index].value : gamepad.axes[index]; const value = type === 'button' ? gamepad.buttons[index].value : gamepad.axes[index];
return axis === 'direction' ? this.toDegrees(value, min, max) : this.toPercentage(value, min, max); return axis === 'direction' ? this.toDegrees(value, min, max) : this.toPercentage(value, min, max);
}, }
loadAxes: function () {
/**
* Loads the axes
*/
loadAxes() {
this.AXES.forEach((axis) => { this.AXES.forEach((axis) => {
this[axis] = { this[axis] = {
[`${axis}Type`]: (gamepad.getUrlParam(`${axis}Type`) || 'axis').replace('axis', 'axe'), [`${axis}Type`]: (gamepad.getUrlParam(`${axis}Type`) || 'axis').replace('axis', 'axe'),
@ -41,19 +67,59 @@ function TelemetryTemplate(gamepad) {
} }
}); });
this.directionDegrees = gamepad.getUrlParam('directionDegrees') || 360; this.directionDegrees = gamepad.getUrlParam('directionDegrees') || 360;
}, }
initChart: function () {
if (!window.google) { /**
window.setTimeout(this.loadGoogleCharts.bind(this), 100); * Initializes the template
*/
init() {
this.$fps = document.querySelector('#fps');
this.$clutchBar = document.querySelector('#clutch .bar');
this.$clutchValue = document.querySelector('#clutch .value');
this.$brakeBar = document.querySelector('#brake .bar');
this.$brakeValue = document.querySelector('#brake .value');
this.$throttleBar = document.querySelector('#throttle .bar');
this.$throttleValue = document.querySelector('#throttle .value');
this.$directionIndicator = document.querySelector('#direction .indicator');
this.AXES = ['clutch', 'brake', 'throttle', 'direction'];
this.frequency = gamepad.getUrlParam('fps') || 60;
this.historyLength = gamepad.getUrlParam('history') || 5000;
this.index = 0;
this.interval = 1000 / this.frequency;
this.length = this.historyLength / this.interval;
this.loadAxes();
this.initChart();
}
/**
* Initializes the live chart
*/
initChart() {
const script = document.createElement('script');
script.async = true;
script.src = `https://www.gstatic.com/charts/loader.js`;
script.onload = () => {
if (!google || !google.visualization) {
this.loadGoogleCharts();
return; return;
} }
this.drawChart(); this.drawChart();
}, };
loadGoogleCharts: function () { this.gamepad.$gamepad.appendChild(script);
}
/**
* Loads the Google Charts library
*/
loadGoogleCharts() {
google.charts.load('current', { packages: ['corechart', 'line'], }); google.charts.load('current', { packages: ['corechart', 'line'], });
google.charts.setOnLoadCallback(this.drawChart.bind(this)); google.charts.setOnLoadCallback(this.drawChart.bind(this));
}, }
drawChart: function () {
/**
* Draws the live chart with the initial data
*/
drawChart() {
this.initialData = [['time', 'clutch', 'brake', 'throttle']]; this.initialData = [['time', 'clutch', 'brake', 'throttle']];
for (this.index = 0; this.index < this.length; this.index++) { for (this.index = 0; this.index < this.length; this.index++) {
this.initialData.push([this.index, 0, 0, 0]); this.initialData.push([this.index, 0, 0, 0]);
@ -91,23 +157,49 @@ function TelemetryTemplate(gamepad) {
}; };
this.chart = new google.visualization.LineChart(document.querySelector('#chart')); this.chart = new google.visualization.LineChart(document.querySelector('#chart'));
this.chart.draw(this.data, this.options); this.chart.draw(this.data, this.options);
this.running = true;
this.update(); this.update();
}, }
update: function () {
const activeGamepad = gamepad.getActive(); /**
* Updates the live chart and the meters
*/
update() {
if (!this.running) return;
const activeGamepad = this.gamepad.getActive();
if (!activeGamepad) return; if (!activeGamepad) return;
const [clutch, brake, throttle, direction] = this.AXES.map((axis) => this.toAxisValue(activeGamepad, axis)); const [clutch, brake, throttle, direction] = this.AXES.map((axis) => this.toAxisValue(activeGamepad, axis));
this.updateChart(clutch, brake, throttle); this.updateChart(clutch, brake, throttle);
this.updateMeters(clutch, brake, throttle, direction); this.updateMeters(clutch, brake, throttle, direction);
window.setTimeout(this.update.bind(this), this.interval); window.setTimeout(this.update.bind(this), this.interval);
}, }
updateChart: function (clutch, brake, throttle) {
/**
* Updates the live chart with the latest data
*
* @param {Number} clutch
* @param {Number} brake
* @param {Number} throttle
*/
updateChart(clutch, brake, throttle) {
this.data.removeRows(0, 1); this.data.removeRows(0, 1);
this.data.addRow([this.index, clutch, brake, throttle]); this.data.addRow([this.index, clutch, brake, throttle]);
this.chart.draw(this.data, this.options); this.chart.draw(this.data, this.options);
this.index++; this.index++;
}, }
updateMeters: function (clutch, brake, throttle, direction) {
/**
* Updates the meters with the latest data
*
* @param {Number} clutch
* @param {Number} brake
* @param {Number} throttle
* @param {Number} direction
*/
updateMeters(clutch, brake, throttle, direction) {
Object.entries({ clutch, brake, throttle, direction }).forEach(([axis, value]) => { Object.entries({ clutch, brake, throttle, direction }).forEach(([axis, value]) => {
if (axis === 'direction') { if (axis === 'direction') {
this.$directionIndicator.style.transform = `rotate(${value}deg)`; this.$directionIndicator.style.transform = `rotate(${value}deg)`;
@ -118,7 +210,4 @@ function TelemetryTemplate(gamepad) {
this[`$${axis}Bar`].style.height = `${value}%`; this[`$${axis}Bar`].style.height = `${value}%`;
}); });
} }
}.init();
}; };
new TelemetryTemplate(window.gamepad);

View File

@ -1,5 +1,3 @@
<link rel="stylesheet" href="templates/xbox-one/template.css">
<script async src="templates/xbox-one/template.js"></script>
<div class="triggers"> <div class="triggers">
<span class="trigger left" data-button="6"></span> <span class="trigger left" data-button="6"></span>
<span class="trigger right" data-button="7"></span> <span class="trigger right" data-button="7"></span>

View File

@ -1,37 +1,34 @@
function XboxOneTemplate(gamepad) { window.gamepad.template = class XboxOneTemplate {
return { /**
init: function () { * Instanciates a new Xbox One controller template
gamepad.updateButton = function ($button) { */
const value = parseFloat($button.attr('data-value'), 10); constructor() {
if ($button.is('.trigger')) { this.gamepad = window.gamepad;
$button.css( this.gamepad.updateButton = ($button) => this.updateButton($button);
gamepad.triggersMeter this.gamepad.updateAxis = ($axis) => this.updateAxis($axis);
? {
opacity: 1,
'clip-path': `inset(${(1 - value) * 100}% 0px 0px 0pc)`,
} }
: {
opacity: `${value * 100}%`, /**
'clip-path': 'none', * Destroys the template
*/
destructor() {
delete this.gamepad.updateButton;
delete this.gamepad.updateAxis;
} }
);
updateButton($button) {
if (!$button.matches('.trigger')) return;
const value = parseFloat($button.getAttribute('data-value'), 10);
$button.style.setProperty('opacity', this.gamepad.triggersMeter ? 1 : `${value * 100}%`);
$button.style.setProperty('clip-path', this.gamepad.triggersMeter ? `inset(${100 - value * 100}% 0px 0px 0pc)` : 'none');
}
updateAxis($axis) {
if (!$axis.matches('.stick')) return;
const axisX = $axis.getAttribute('data-value-x');
const axisY = $axis.getAttribute('data-value-y');
$axis.style.setProperty('margin-top', `${axisY * 25}px`);
$axis.style.setProperty('margin-left', `${axisX * 25}px`);
$axis.style.setProperty('transform', `rotateX(${-parseFloat(axisY * 30, 8)}deg) rotateY(${parseFloat(axisX * 30, 8)}deg)`);
} }
}; };
gamepad.updateAxis = function ($axis) {
const axisX = $axis.attr('data-value-x');
const axisY = $axis.attr('data-value-y');
if ($axis.is('.stick')) {
$axis.css({
'margin-top': axisY * 25,
'margin-left': axisX * 25,
transform: `rotateX(${-parseFloat(
axisY * 30,
8
)}deg) rotateY(${parseFloat(axisX * 30, 8)}deg)`,
});
}
};
},
}.init();
};
new XboxOneTemplate(window.gamepad);