telemetry wizard
This commit is contained in:
parent
9f765b671f
commit
0fcb7c1afc
@ -99,6 +99,11 @@ body.unsupported #gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#gamepad {
|
#gamepad {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad .controller {
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
|
@ -759,7 +759,7 @@ class Gamepad {
|
|||||||
script.src = `templates/${this.type}/template.js`;
|
script.src = `templates/${this.type}/template.js`;
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
// initialize the template
|
// initialize the template
|
||||||
new this.template();
|
this.template = new this.templateClass();
|
||||||
|
|
||||||
// enqueue the initial display refresh
|
// enqueue the initial display refresh
|
||||||
this.startTemplate();
|
this.startTemplate();
|
||||||
@ -1087,7 +1087,7 @@ class Gamepad {
|
|||||||
// update the DOM with the zoom value
|
// update the DOM with the zoom value
|
||||||
this.$gamepad.style.setProperty(
|
this.$gamepad.style.setProperty(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(-50%, -50%) scale(${this.zoomLevel}, ${this.zoomLevel})`
|
`scale(${this.zoomLevel}, ${this.zoomLevel})`
|
||||||
);
|
);
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
@ -1127,7 +1127,7 @@ class Gamepad {
|
|||||||
*
|
*
|
||||||
* @param {boolean|undefined} debug
|
* @param {boolean|undefined} debug
|
||||||
*/
|
*/
|
||||||
toggleDebug(debug = null) {
|
toggleDebug(debug) {
|
||||||
// ensure that a gamepad is currently active
|
// ensure that a gamepad is currently active
|
||||||
if (this.index === null) return;
|
if (this.index === null) return;
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<div class="controller">
|
||||||
<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">
|
||||||
@ -60,3 +61,4 @@
|
|||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div class="container"></div>
|
<div class="container"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
window.gamepad.template = class DebugTemplate {
|
window.gamepad.templateClass = class DebugTemplate {
|
||||||
/**
|
/**
|
||||||
* Instanciates a new debug template
|
* Instanciates a new debug template
|
||||||
*/
|
*/
|
||||||
@ -98,7 +98,7 @@ window.gamepad.template = class DebugTemplate {
|
|||||||
* Updates the value of an element
|
* Updates the value of an element
|
||||||
*
|
*
|
||||||
* @param {Element} $elem
|
* @param {Element} $elem
|
||||||
* @param {Number} precision
|
* @param {number} precision
|
||||||
*/
|
*/
|
||||||
updateElem($elem, value, precision = 2) {
|
updateElem($elem, value, precision = 2) {
|
||||||
this.updateTimestamp();
|
this.updateTimestamp();
|
||||||
|
@ -1,67 +1,67 @@
|
|||||||
#gamepad {
|
.controller {
|
||||||
width: 806px;
|
width: 806px;
|
||||||
height: 598px;
|
height: 598px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad[data-color="black"] {
|
#gamepad[data-color="black"] .controller {
|
||||||
background-image: url(base-black.svg);
|
background-image: url(base-black.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad[data-color="white"] {
|
#gamepad[data-color="white"] .controller {
|
||||||
background-image: url(base-white.svg);
|
background-image: url(base-white.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad[data-color="red"] {
|
#gamepad[data-color="red"] .controller {
|
||||||
background-image: url(base-red.svg);
|
background-image: url(base-red.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad[data-color="blue"] {
|
#gamepad[data-color="blue"] .controller {
|
||||||
background-image: url(base-blue.svg);
|
background-image: url(base-blue.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad.disconnected {
|
#gamepad.disconnected .controller {
|
||||||
background-image: url(disconnected.svg);
|
background-image: url(disconnected.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad.disconnected div {
|
#gamepad.disconnected .controller div {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .triggers {
|
.controller .triggers {
|
||||||
width: 588px;
|
width: 588px;
|
||||||
height: 90px;
|
height: 90px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 109px;
|
left: 109px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .trigger {
|
.controller .trigger {
|
||||||
width: 99px;
|
width: 99px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: url(triggers.svg);
|
background: url(triggers.svg);
|
||||||
clip-path: inset(100% 0px 0px 0pc);
|
clip-path: inset(100% 0px 0px 0pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .trigger[data-value="0"] {
|
.controller .trigger[data-value="0"] {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .trigger.left {
|
.controller .trigger.left {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .trigger.right {
|
.controller .trigger.right {
|
||||||
float: right;
|
float: right;
|
||||||
background-position-x: 99px;
|
background-position-x: 99px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumper {
|
.controller .bumper {
|
||||||
width: 99px;
|
width: 99px;
|
||||||
height: 23px;
|
height: 23px;
|
||||||
background: url(bumper.svg) no-repeat;
|
background: url(bumper.svg) no-repeat;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumpers {
|
.controller .bumpers {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 588px;
|
width: 588px;
|
||||||
height: 23px;
|
height: 23px;
|
||||||
@ -69,22 +69,22 @@
|
|||||||
top: 94px;
|
top: 94px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumper[data-pressed="true"] {
|
.controller .bumper[data-pressed="true"] {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumper.left {
|
.controller .bumper.left {
|
||||||
/* -webkit-transform: rotateY(180deg); */
|
/* -webkit-transform: rotateY(180deg); */
|
||||||
/* transform: rotateY(180deg); */
|
/* transform: rotateY(180deg); */
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumper.right {
|
.controller .bumper.right {
|
||||||
float: right;
|
float: right;
|
||||||
transform: rotateY(180deg);
|
transform: rotateY(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .touchpad {
|
.controller .touchpad {
|
||||||
width: 262px;
|
width: 262px;
|
||||||
height: 151px;
|
height: 151px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -92,11 +92,11 @@
|
|||||||
top: 122px;
|
top: 122px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .touchpad[data-pressed="true"] {
|
.controller .touchpad[data-pressed="true"] {
|
||||||
background: url(touchpad.svg) no-repeat center;
|
background: url(touchpad.svg) no-repeat center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .meta {
|
.controller .meta {
|
||||||
width: 42px;
|
width: 42px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -104,11 +104,11 @@
|
|||||||
bottom: 216px;
|
bottom: 216px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .meta[data-pressed="true"] {
|
.controller .meta[data-pressed="true"] {
|
||||||
background: url(meta.svg) no-repeat center;
|
background: url(meta.svg) no-repeat center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .arrows {
|
.controller .arrows {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 352px;
|
width: 352px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
@ -116,29 +116,29 @@
|
|||||||
left: 227px;
|
left: 227px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .select,
|
.controller .select,
|
||||||
#gamepad .start {
|
.controller .start {
|
||||||
background: url(start.svg);
|
background: url(start.svg);
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .select[data-pressed="true"],
|
.controller .select[data-pressed="true"],
|
||||||
#gamepad .start[data-pressed="true"] {
|
.controller .start[data-pressed="true"] {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .select {
|
.controller .select {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .start {
|
.controller .start {
|
||||||
float: right;
|
float: right;
|
||||||
background-position: 28px 0;
|
background-position: 28px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .buttons {
|
.controller .buttons {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 170px;
|
width: 170px;
|
||||||
height: 170px;
|
height: 170px;
|
||||||
@ -146,40 +146,40 @@
|
|||||||
left: 567px;
|
left: 567px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .button {
|
.controller .button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 56px;
|
width: 56px;
|
||||||
height: 56px;
|
height: 56px;
|
||||||
background: url(buttons.svg);
|
background: url(buttons.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .button[data-pressed="true"] {
|
.controller .button[data-pressed="true"] {
|
||||||
background-position-y: 56px;
|
background-position-y: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .a {
|
.controller .a {
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
left: 56px;
|
left: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .b {
|
.controller .b {
|
||||||
background-position: -56px 0;
|
background-position: -56px 0;
|
||||||
top: 56px;
|
top: 56px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .x {
|
.controller .x {
|
||||||
background-position: 112px 0;
|
background-position: 112px 0;
|
||||||
top: 56px;
|
top: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .y {
|
.controller .y {
|
||||||
background-position: 56px 0;
|
background-position: 56px 0;
|
||||||
left: 56px;
|
left: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .sticks {
|
.controller .sticks {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 361px;
|
width: 361px;
|
||||||
height: 105px;
|
height: 105px;
|
||||||
@ -187,32 +187,32 @@
|
|||||||
left: 228px;
|
left: 228px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick {
|
.controller .stick {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: url(sticks.svg);
|
background: url(sticks.svg);
|
||||||
height: 94px;
|
height: 94px;
|
||||||
width: 94px;
|
width: 94px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick[data-pressed="true"].left {
|
.controller .stick[data-pressed="true"].left {
|
||||||
background-position-x: -96px;
|
background-position-x: -96px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick[data-pressed="true"].right {
|
.controller .stick[data-pressed="true"].right {
|
||||||
background-position-x: -192px;
|
background-position-x: -192px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick.left {
|
.controller .stick.left {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick.right {
|
.controller .stick.right {
|
||||||
top: calc(100% - 105px);
|
top: calc(100% - 105px);
|
||||||
left: calc(100% - 105px);
|
left: calc(100% - 105px);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .dpad {
|
.controller .dpad {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 125px;
|
width: 125px;
|
||||||
height: 126px;
|
height: 126px;
|
||||||
@ -220,52 +220,52 @@
|
|||||||
left: 92px;
|
left: 92px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face {
|
.controller .face {
|
||||||
background: url(dpad.svg);
|
background: url(dpad.svg);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.up,
|
.controller .face.up,
|
||||||
#gamepad .face.down {
|
.controller .face.down {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 52px;
|
height: 52px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.left,
|
.controller .face.left,
|
||||||
#gamepad .face.right {
|
.controller .face.right {
|
||||||
width: 52px;
|
width: 52px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.up {
|
.controller .face.up {
|
||||||
left: 44px;
|
left: 44px;
|
||||||
top: 0;
|
top: 0;
|
||||||
background-position: -37px 0px;
|
background-position: -37px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.down {
|
.controller .face.down {
|
||||||
left: 44px;
|
left: 44px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-position: 0px 0;
|
background-position: 0px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.left {
|
.controller .face.left {
|
||||||
top: 44px;
|
top: 44px;
|
||||||
left: 0;
|
left: 0;
|
||||||
background-position: 104px 0;
|
background-position: 104px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.right {
|
.controller .face.right {
|
||||||
top: 44px;
|
top: 44px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
background-position: 52px 0;
|
background-position: 52px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face[data-pressed="true"] {
|
.controller .face[data-pressed="true"] {
|
||||||
/* margin-top: 5px; */
|
/* margin-top: 5px; */
|
||||||
background-position-y: 52px;
|
background-position-y: 52px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad.half {
|
.controller.half {
|
||||||
margin-top: -300px;
|
margin-top: -300px;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<div class="controller">
|
||||||
<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>
|
||||||
@ -31,3 +32,4 @@
|
|||||||
<span class="face left" data-button="14"></span>
|
<span class="face left" data-button="14"></span>
|
||||||
<span class="face right" data-button="15"></span>
|
<span class="face right" data-button="15"></span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
window.gamepad.template = class DualShock4Template {
|
window.gamepad.templateClass = class DualShock4Template {
|
||||||
/**
|
/**
|
||||||
* Instanciates a new DualShock 4 controller template
|
* Instanciates a new DualShock 4 controller template
|
||||||
*/
|
*/
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
#gamepad {
|
.controller {
|
||||||
height: 700px;
|
height: 700px;
|
||||||
width: 1200px;
|
width: 1200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad[data-color="black"] {
|
#gamepad[data-color="black"] .controller {
|
||||||
background-image: url(base-black.png);
|
background-image: url(base-black.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad[data-color="white"] {
|
#gamepad[data-color="white"] .controller {
|
||||||
background-image: url(base-white.png);
|
background-image: url(base-white.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad.disconnected div {
|
#gamepad.disconnected .controller {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<div class="controller">
|
||||||
<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>
|
||||||
@ -31,3 +32,4 @@
|
|||||||
<span class="face left" data-button="14"></span>
|
<span class="face left" data-button="14"></span>
|
||||||
<span class="face right" data-button="15"></span>
|
<span class="face right" data-button="15"></span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
window.gamepad.template = class DualSenseTemplate {
|
window.gamepad.templateClass = class DualSenseTemplate {
|
||||||
/**
|
/**
|
||||||
* Instanciates a new DualSense controller template
|
* Instanciates a new DualSense controller template
|
||||||
*/
|
*/
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
#gamepad #telemetry {
|
#gamepad #telemetry {
|
||||||
background: var(--main-bg-color);
|
background: var(--main-bg-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 650px;
|
|
||||||
height: 120px;
|
height: 120px;
|
||||||
border-top-left-radius: 6px;
|
padding: 4px;
|
||||||
border-bottom-left-radius: 6px;
|
border-radius: 6px;
|
||||||
border-top-right-radius: 60px;
|
|
||||||
border-bottom-right-radius: 60px;
|
|
||||||
|
|
||||||
--black-color: black;
|
--black-color: black;
|
||||||
--white-color: white;
|
--white-color: white;
|
||||||
@ -17,13 +14,23 @@
|
|||||||
--throttle-color: #0CA818;
|
--throttle-color: #0CA818;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry.with-steering {
|
||||||
|
border-top-right-radius: 60px;
|
||||||
|
border-bottom-right-radius: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry,
|
||||||
#gamepad #telemetry * {
|
#gamepad #telemetry * {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry>*+* {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #chart {
|
#gamepad #telemetry #chart {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin: 4px;
|
width: 400px;
|
||||||
background-color: var(--main-component-color);
|
background-color: var(--main-component-color);
|
||||||
border: 1px solid var(--black-color);
|
border: 1px solid var(--black-color);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -32,17 +39,20 @@
|
|||||||
#gamepad #telemetry #meters {
|
#gamepad #telemetry #meters {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
width: 90px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #meters .meter {
|
#gamepad #telemetry #meters .meter {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin: 4px 2px;
|
|
||||||
background-color: var(--main-component-color);
|
background-color: var(--main-component-color);
|
||||||
border: 1px solid var(--black-color);
|
border: 1px solid var(--black-color);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamepad #telemetry #meters .meter+.meter {
|
||||||
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #meters .meter .value {
|
#gamepad #telemetry #meters .meter .value {
|
||||||
@ -62,28 +72,31 @@
|
|||||||
height: 0%;
|
height: 0%;
|
||||||
transition: height 100ms;
|
transition: height 100ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #meters #clutch.meter .bar {
|
#gamepad #telemetry #meters #clutch.meter .bar {
|
||||||
background-color: var(--clutch-color);
|
background-color: var(--clutch-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #meters #brake.meter .bar {
|
#gamepad #telemetry #meters #brake.meter .bar {
|
||||||
background-color: var(--brake-color);
|
background-color: var(--brake-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #meters #throttle.meter .bar {
|
#gamepad #telemetry #meters #throttle.meter .bar {
|
||||||
background-color: var(--throttle-color);
|
background-color: var(--throttle-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #direction {
|
#gamepad #telemetry #steering {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
margin: 10px;
|
margin: 6px;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
background-color: var(--main-component-color);
|
background-color: var(--main-component-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #direction .center {
|
#gamepad #telemetry #steering .center {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 20%;
|
top: 20%;
|
||||||
left: 20%;
|
left: 20%;
|
||||||
@ -93,7 +106,7 @@
|
|||||||
background-color: var(--main-bg-color);
|
background-color: var(--main-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #direction .indicator {
|
#gamepad #telemetry #steering .indicator {
|
||||||
display: block;
|
display: block;
|
||||||
width: 5%;
|
width: 5%;
|
||||||
height: 50%;
|
height: 50%;
|
||||||
@ -101,3 +114,36 @@
|
|||||||
transform-origin: bottom;
|
transform-origin: bottom;
|
||||||
transition: transform 100ms;
|
transition: transform 100ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#wizard #wizard-instructions h4,
|
||||||
|
#wizard #wizard-instructions p {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wizard #wizard-instructions h4 {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wizard .wizard-options {
|
||||||
|
display: flex;
|
||||||
|
margin: auto;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wizard .wizard-options div {
|
||||||
|
margin: 0 8px;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wizard .wizard-options div label,
|
||||||
|
#wizard .wizard-options div input {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wizard .wizard-options input[name="history-length-option"] {
|
||||||
|
width: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wizard .wizard-options input[name="steering-angle-option"] {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<div id="fps"></div>
|
<div id="telemetry" class="controller">
|
||||||
<div id="telemetry">
|
|
||||||
<div id="chart"></div>
|
<div id="chart"></div>
|
||||||
<div id="meters">
|
<div id="meters">
|
||||||
<div id="clutch" class="meter">
|
<div id="clutch" class="meter">
|
||||||
@ -15,8 +14,14 @@
|
|||||||
<div class="value">0</div>
|
<div class="value">0</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="direction">
|
<div id="steering">
|
||||||
<div class="indicator"></div>
|
<div class="indicator"></div>
|
||||||
<div class="center"></div>
|
<div class="center"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="wizard" class="popout">
|
||||||
|
<div class="content">
|
||||||
|
<h2>Telemetry Wizard</h2>
|
||||||
|
<div id="wizard-instructions"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
window.gamepad.template = class TelemetryTemplate {
|
gamepad.templateClass = class TelemetryTemplate {
|
||||||
/**
|
/**
|
||||||
* Instanciates a new telemetry template
|
* Instanciates a new telemetry template
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.AXES = ['clutch', 'brake', 'throttle', 'steering'];
|
||||||
this.gamepad = window.gamepad;
|
this.gamepad = window.gamepad;
|
||||||
|
|
||||||
|
this.loadSelectors();
|
||||||
|
this.loadUrlParams();
|
||||||
|
|
||||||
|
if (!this.AXES.some((axis) => this[axis].index)) {
|
||||||
|
this.wizard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,28 +27,28 @@ window.gamepad.template = class TelemetryTemplate {
|
|||||||
/**
|
/**
|
||||||
* Converts a value to a percentage
|
* Converts a value to a percentage
|
||||||
*
|
*
|
||||||
* @param {Number} value
|
* @param {number} value
|
||||||
* @param {Number} min
|
* @param {number} min
|
||||||
* @param {Number} max
|
* @param {number} max
|
||||||
* @returns {Number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
toPercentage(value, min, max) {
|
toPercentage(value, min, max) {
|
||||||
return value !== undefined
|
return value !== undefined
|
||||||
? Math.round((value - min) * (100 / (max - min)))
|
? Math.max(0, Math.min(100, Math.round((value - min) * (100 / (max - min)))))
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a value to degrees
|
* Converts a value to degrees
|
||||||
*
|
*
|
||||||
* @param {Number} value
|
* @param {number} value
|
||||||
* @param {Number} min
|
* @param {number} min
|
||||||
* @param {Number} max
|
* @param {number} max
|
||||||
* @returns {Number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
toDegrees(value, min, max) {
|
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.angle) * (percentage - 50) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,85 +56,128 @@ window.gamepad.template = class TelemetryTemplate {
|
|||||||
*
|
*
|
||||||
* @param {object} gamepad
|
* @param {object} gamepad
|
||||||
* @param {string} axis
|
* @param {string} axis
|
||||||
* @returns {Number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
toAxisValue(gamepad, axis) {
|
toAxisValue(gamepad, axis) {
|
||||||
const { [`${axis}Type`]: type, [`${axis}Index`]: index, [`${axis}Min`]: min, [`${axis}Max`]: max } = this[axis];
|
const { type, index, min, max } = this[axis];
|
||||||
|
if (!type || !index) return null;
|
||||||
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 === 'steering' ? this.toDegrees(value, min, max) : this.toPercentage(value, min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the axes
|
* Loads the DOM selectors
|
||||||
*/
|
*/
|
||||||
loadAxes() {
|
loadSelectors() {
|
||||||
|
this.$telemetry = document.querySelector('#telemetry');
|
||||||
|
this.$chart = this.$telemetry.querySelector('#chart');
|
||||||
|
this.$meters = this.$telemetry.querySelector('#meters');
|
||||||
|
this.$clutch = this.$telemetry.querySelector('#clutch');
|
||||||
|
this.$clutchBar = this.$clutch.querySelector('.bar');
|
||||||
|
this.$clutchValue = this.$clutch.querySelector('.value');
|
||||||
|
this.$brake = this.$telemetry.querySelector('#brake');
|
||||||
|
this.$brakeBar = this.$brake.querySelector('.bar');
|
||||||
|
this.$brakeValue = this.$brake.querySelector('.value');
|
||||||
|
this.$throttle = this.$telemetry.querySelector('#throttle');
|
||||||
|
this.$throttleBar = this.$throttle.querySelector('.bar');
|
||||||
|
this.$throttleValue = this.$throttle.querySelector('.value');
|
||||||
|
this.$steering = this.$telemetry.querySelector('#steering');
|
||||||
|
this.$steeringIndicator = this.$steering.querySelector('.indicator');
|
||||||
|
this.$wizard = document.querySelector('#wizard');
|
||||||
|
this.$wizardInstructions = this.$wizard.querySelector('#wizard-instructions');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the params from the URL
|
||||||
|
*/
|
||||||
|
loadUrlParams() {
|
||||||
|
this.withChart = gamepad.getUrlParam('chart') !== 'false';
|
||||||
|
this.historyLength = gamepad.getUrlParam('history') || 5000;
|
||||||
|
|
||||||
|
this.withMeters = gamepad.getUrlParam('meters') !== 'false';
|
||||||
|
|
||||||
|
this.withSteering = gamepad.getUrlParam('steeringIndex') !== null;
|
||||||
|
this.angle = gamepad.getUrlParam('angle') || 360;
|
||||||
|
|
||||||
|
this.frequency = gamepad.getUrlParam('fps') || 60;
|
||||||
|
|
||||||
this.AXES.forEach((axis) => {
|
this.AXES.forEach((axis) => {
|
||||||
this[axis] = {
|
this[axis] = {
|
||||||
[`${axis}Type`]: (gamepad.getUrlParam(`${axis}Type`) || 'axis').replace('axis', 'axe'),
|
type: (gamepad.getUrlParam(`${axis}Type`) || 'axis').replace('axis', 'axe'),
|
||||||
[`${axis}Index`]: gamepad.getUrlParam(`${axis}Index`),
|
index: gamepad.getUrlParam(`${axis}Index`),
|
||||||
[`${axis}Min`]: gamepad.getUrlParam(`${axis}Min`) || -1,
|
min: gamepad.getUrlParam(`${axis}Min`) || -1,
|
||||||
[`${axis}Max`]: gamepad.getUrlParam(`${axis}Max`) || 1,
|
max: gamepad.getUrlParam(`${axis}Max`) || 1,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.directionDegrees = gamepad.getUrlParam('directionDegrees') || 360;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the template
|
* Sets up the template
|
||||||
*/
|
*/
|
||||||
init() {
|
setupTemplate() {
|
||||||
this.$fps = document.querySelector('#fps');
|
if (!this.withChart) this.$chart.remove();
|
||||||
this.$clutchBar = document.querySelector('#clutch .bar');
|
|
||||||
this.$clutchValue = document.querySelector('#clutch .value');
|
if (!this.clutch.index) this.$clutch.remove();
|
||||||
this.$brakeBar = document.querySelector('#brake .bar');
|
if (!this.brake.index) this.$brake.remove();
|
||||||
this.$brakeValue = document.querySelector('#brake .value');
|
if (!this.throttle.index) this.$throttle.remove();
|
||||||
this.$throttleBar = document.querySelector('#throttle .bar');
|
if (!this.withMeters) this.$meters.remove();
|
||||||
this.$throttleValue = document.querySelector('#throttle .value');
|
|
||||||
this.$directionIndicator = document.querySelector('#direction .indicator');
|
if (!this.steering.index) {
|
||||||
this.AXES = ['clutch', 'brake', 'throttle', 'direction'];
|
this.$steering.remove();
|
||||||
this.frequency = gamepad.getUrlParam('fps') || 60;
|
} else {
|
||||||
this.historyLength = gamepad.getUrlParam('history') || 5000;
|
this.$telemetry.classList.add('with-steering');
|
||||||
this.index = 0;
|
}
|
||||||
this.interval = 1000 / this.frequency;
|
|
||||||
this.length = this.historyLength / this.interval;
|
|
||||||
this.loadAxes();
|
|
||||||
this.initChart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async setupChart() {
|
||||||
* Initializes the live chart
|
if (!this.withChart) return;
|
||||||
*/
|
return new Promise((resolve) => {
|
||||||
initChart() {
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.async = true;
|
script.async = true;
|
||||||
script.src = `https://www.gstatic.com/charts/loader.js`;
|
script.src = `https://www.gstatic.com/charts/loader.js`;
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
if (!google || !google.visualization) {
|
if (!google || !google.visualization) {
|
||||||
this.loadGoogleCharts();
|
this.loadGoogleCharts(resolve);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.drawChart();
|
this.drawChart(resolve);
|
||||||
};
|
};
|
||||||
this.gamepad.$gamepad.appendChild(script);
|
this.gamepad.$gamepad.appendChild(script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the live chart
|
||||||
|
*/
|
||||||
|
async init() {
|
||||||
|
this.interval = 1000 / this.frequency;
|
||||||
|
this.length = this.historyLength / this.interval;
|
||||||
|
|
||||||
|
this.setupTemplate();
|
||||||
|
await this.setupChart();
|
||||||
|
|
||||||
|
this.running = true;
|
||||||
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the Google Charts library
|
* Loads the Google Charts library
|
||||||
*/
|
*/
|
||||||
loadGoogleCharts() {
|
loadGoogleCharts(resolve) {
|
||||||
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, resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the live chart with the initial data
|
* Draws the live chart with the initial data and starts the draw update loop
|
||||||
*/
|
*/
|
||||||
drawChart() {
|
drawChart(resolve) {
|
||||||
this.initialData = [['time', 'clutch', 'brake', 'throttle']];
|
const now = Date.now();
|
||||||
for (this.index = 0; this.index < this.length; this.index++) {
|
const initialData = [['time', 'clutch', 'brake', 'throttle']];
|
||||||
this.initialData.push([this.index, 0, 0, 0]);
|
for (let index = now - this.historyLength; index < now; index += this.interval) {
|
||||||
|
initialData.push([index, 0, 0, 0]);
|
||||||
}
|
}
|
||||||
this.data = google.visualization.arrayToDataTable(this.initialData);
|
this.data = google.visualization.arrayToDataTable(initialData);
|
||||||
this.options = {
|
this.options = {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
chartArea: {
|
chartArea: {
|
||||||
@ -138,6 +191,10 @@ window.gamepad.template = class TelemetryTemplate {
|
|||||||
textPosition: 'none',
|
textPosition: 'none',
|
||||||
gridlines: {
|
gridlines: {
|
||||||
color: 'transparent',
|
color: 'transparent',
|
||||||
|
},
|
||||||
|
viewWindow: {
|
||||||
|
min: now - this.historyLength,
|
||||||
|
max: now
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
vAxis: {
|
vAxis: {
|
||||||
@ -153,13 +210,14 @@ window.gamepad.template = class TelemetryTemplate {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
colors: ['#2D64B9', '#A52725', '#0CA818'],
|
colors: ['#2D64B9', '#A52725', '#0CA818'],
|
||||||
legend: 'none'
|
legend: 'none',
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'none'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
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);
|
||||||
|
resolve();
|
||||||
this.running = true;
|
|
||||||
this.update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,11 +226,11 @@ window.gamepad.template = class TelemetryTemplate {
|
|||||||
update() {
|
update() {
|
||||||
if (!this.running) return;
|
if (!this.running) return;
|
||||||
|
|
||||||
const activeGamepad = this.gamepad.getActive();
|
const gamepad = this.gamepad.getActive();
|
||||||
if (!activeGamepad) return;
|
const [clutch, brake, throttle, steering] = this.AXES.map((axis) => this.toAxisValue(gamepad, axis));
|
||||||
const [clutch, brake, throttle, direction] = this.AXES.map((axis) => this.toAxisValue(activeGamepad, axis));
|
if (this.withChart) this.updateChart(clutch, brake, throttle);
|
||||||
this.updateChart(clutch, brake, throttle);
|
if (this.withMeters) this.updateMeters(clutch, brake, throttle);
|
||||||
this.updateMeters(clutch, brake, throttle, direction);
|
if (this.withSteering) this.updateSteering(steering);
|
||||||
|
|
||||||
window.setTimeout(this.update.bind(this), this.interval);
|
window.setTimeout(this.update.bind(this), this.interval);
|
||||||
}
|
}
|
||||||
@ -180,34 +238,350 @@ window.gamepad.template = class TelemetryTemplate {
|
|||||||
/**
|
/**
|
||||||
* Updates the live chart with the latest data
|
* Updates the live chart with the latest data
|
||||||
*
|
*
|
||||||
* @param {Number} clutch
|
* @param {number} clutch
|
||||||
* @param {Number} brake
|
* @param {number} brake
|
||||||
* @param {Number} throttle
|
* @param {number} throttle
|
||||||
*/
|
*/
|
||||||
updateChart(clutch, brake, throttle) {
|
updateChart(clutch, brake, throttle) {
|
||||||
this.data.removeRows(0, 1);
|
const now = Date.now();
|
||||||
this.data.addRow([this.index, clutch, brake, throttle]);
|
this.data.removeRows(0, this.data.getFilteredRows([{ column: 0, maxValue: now - this.historyLength }]).length);
|
||||||
|
this.data.addRow([now, clutch, brake, throttle]);
|
||||||
|
this.options.hAxis.viewWindow = {
|
||||||
|
min: now - this.historyLength,
|
||||||
|
max: now
|
||||||
|
};
|
||||||
this.chart.draw(this.data, this.options);
|
this.chart.draw(this.data, this.options);
|
||||||
this.index++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the meters with the latest data
|
* Updates the meters with the latest data
|
||||||
*
|
*
|
||||||
* @param {Number} clutch
|
* @param {number} clutch
|
||||||
* @param {Number} brake
|
* @param {number} brake
|
||||||
* @param {Number} throttle
|
* @param {number} throttle
|
||||||
* @param {Number} direction
|
* @param {number} steering
|
||||||
*/
|
*/
|
||||||
updateMeters(clutch, brake, throttle, direction) {
|
updateMeters(clutch, brake, throttle) {
|
||||||
Object.entries({ clutch, brake, throttle, direction }).forEach(([axis, value]) => {
|
Object.entries({ clutch, brake, throttle }).forEach(([axis, value]) => {
|
||||||
if (axis === 'direction') {
|
if (value === null) return;
|
||||||
this.$directionIndicator.style.transform = `rotate(${value}deg)`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this[`$${axis}Value`].innerHTML = value;
|
this[`$${axis}Value`].innerHTML = value;
|
||||||
this[`$${axis}Value`].style.opacity = `${Math.round(33 + (value / 1.5))}%`;
|
this[`$${axis}Value`].style.opacity = `${Math.round(33 + (value / 1.5))}%`;
|
||||||
this[`$${axis}Bar`].style.height = `${value}%`;
|
this[`$${axis}Bar`].style.height = `${value}%`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the steering indicator with the latest data
|
||||||
|
*
|
||||||
|
* @param {number} steering
|
||||||
|
*/
|
||||||
|
updateSteering(steering) {
|
||||||
|
this.$steeringIndicator.style.transform = `rotate(${steering}deg)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for one or all buttons of a gamepad to be released
|
||||||
|
*
|
||||||
|
* @param {number} [index]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async waitButtonRelease(index = undefined) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const interval = window.setInterval(() => {
|
||||||
|
const gamepad = this.gamepad.getActive();
|
||||||
|
const pressedButton = index !== undefined
|
||||||
|
? gamepad.buttons[index].pressed
|
||||||
|
: gamepad.buttons.some((button) => button.pressed);
|
||||||
|
if (pressedButton) return;
|
||||||
|
window.clearInterval(interval);
|
||||||
|
resolve();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for a button to be pressed, then released
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async waitButtonClick() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const pressInterval = window.setInterval(() => {
|
||||||
|
const gamepad = this.gamepad.getActive();
|
||||||
|
const index = gamepad.buttons.findIndex((button) => button.pressed);
|
||||||
|
if (index === -1) return;
|
||||||
|
window.clearInterval(pressInterval);
|
||||||
|
const releaseInterval = window.setInterval(() => {
|
||||||
|
const gamepad = this.gamepad.getActive();
|
||||||
|
if (gamepad.buttons[index].pressed) return;
|
||||||
|
window.clearInterval(releaseInterval);
|
||||||
|
resolve(index);
|
||||||
|
}, 100);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for an axis to be pushed out, and of the reference value, if any
|
||||||
|
*
|
||||||
|
* @param {number} index
|
||||||
|
* @param {number} [referenceValue]
|
||||||
|
* @param {number} [duration]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async getAxisPush(index, referenceValue = undefined, duration = 1000) {
|
||||||
|
let start;
|
||||||
|
let value;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const interval = window.setInterval(() => {
|
||||||
|
const gamepad = this.gamepad.getActive();
|
||||||
|
if (start === undefined) {
|
||||||
|
start = Date.now();
|
||||||
|
value = gamepad.axes[index];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (referenceValue !== undefined && Math.abs(gamepad.axes[index] - referenceValue) < 0.5) {
|
||||||
|
}
|
||||||
|
if (Math.abs(gamepad.axes[index] - value) > 0.05) {
|
||||||
|
start = undefined;
|
||||||
|
value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Date.now() - start < duration) {
|
||||||
|
value = gamepad.axes[index];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.clearInterval(interval);
|
||||||
|
resolve(value);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capitalizes a word
|
||||||
|
*
|
||||||
|
* @param {string} word
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
capitalize(word) {
|
||||||
|
return `${word.charAt(0).toUpperCase()}${word.slice(1)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the user for the telemetry options
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async askForOptions() {
|
||||||
|
this.$wizardInstructions.innerHTML = `
|
||||||
|
<h4>Inputs</h4>
|
||||||
|
<div class="wizard-options">
|
||||||
|
<div>
|
||||||
|
<input id="clutch-option" name="clutch-option" type="checkbox" checked>
|
||||||
|
<label for="clutch-option">Clutch</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="brake-option" name="brake-option" type="checkbox" readonly checked>
|
||||||
|
<label for="brake-option">Brake</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="throttle-option" name="throttle-option" type="checkbox" checked>
|
||||||
|
<label for="throttle-option">Throttle</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="steering-option" name="steering-option" type="checkbox" checked>
|
||||||
|
<label for="steering-option">Steering</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h4>Chart widget</h4>
|
||||||
|
<div class="wizard-options">
|
||||||
|
<div>
|
||||||
|
<input id="chart-option" name="chart-option" type="checkbox" checked>
|
||||||
|
<label for="chart-option">Enable</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="history-option">History</label>
|
||||||
|
<input id="history-option" name="history-option" type="number" min="1" max="30" step="1" value="5">s
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h4>Meters widget</h4>
|
||||||
|
<div class="wizard-options">
|
||||||
|
<div>
|
||||||
|
<input id="meters-option" name="meters-option" type="checkbox" checked>
|
||||||
|
<label for="meters-option">Enable</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h4>Steering widget</h4>
|
||||||
|
<div class="wizard-options">
|
||||||
|
<div>
|
||||||
|
<label for="steering-angle-option">Angle</label>
|
||||||
|
<input id="steering-angle-option" name="steering-angle-option" type="number" min="180" max="1080" step="10" value="360">°
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h4>Display mode</h4><div class="wizard-options">
|
||||||
|
<div>
|
||||||
|
<label for="fps-option">Mode</label>
|
||||||
|
<select id="fps-option" name="fps-option">
|
||||||
|
<option value="30">30 FPS (performance)</option>
|
||||||
|
<option value="60" selected>60 FPS (quality)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>Then, press any button to continue.</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
await this.waitButtonRelease();
|
||||||
|
await this.waitButtonClick();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...this.AXES.reduce((options, axis) => {
|
||||||
|
options[`with${this.capitalize(axis)}`] = document.querySelector(`[name=${axis}-option]`).checked;
|
||||||
|
return options;
|
||||||
|
}, {}),
|
||||||
|
chart: document.querySelector('[name=chart-option]').checked,
|
||||||
|
history: parseInt(document.querySelector('[name=history-option]').value) * 1000,
|
||||||
|
meters: document.querySelector('[name=meters-option]').checked,
|
||||||
|
angle: this.$wizardInstructions.querySelector('input[name="steering-angle-option"]').value,
|
||||||
|
fps: parseInt(document.querySelector('[name=fps-option]').value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects activity on any button or axis of the gamepad
|
||||||
|
*
|
||||||
|
* @param {string} [type='button'|'axis'|undefined]
|
||||||
|
* @param {number} [distance=0.5]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async detectActivity(type = undefined, distance = 0.5) {
|
||||||
|
const before = this.gamepad.getActive();
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const interval = window.setInterval(async () => {
|
||||||
|
const gamepad = this.gamepad.getActive();
|
||||||
|
const buttonIndex = ['button', undefined].includes(type)
|
||||||
|
? gamepad.buttons.findIndex((button, index) => button.pressed && Math.abs(button.value - before.buttons[index].value) > distance)
|
||||||
|
: -1;
|
||||||
|
const axisIndex = ['axis', undefined].includes(type)
|
||||||
|
? gamepad.axes.findIndex((axis, index) => Math.abs(axis - before.axes[index]) > distance)
|
||||||
|
: -1;
|
||||||
|
if (buttonIndex === -1 && axisIndex === -1) return;
|
||||||
|
window.clearInterval(interval);
|
||||||
|
resolve({
|
||||||
|
type: buttonIndex === -1 ? 'axis' : 'button',
|
||||||
|
index: buttonIndex === -1 ? axisIndex : buttonIndex,
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calibrates a pedal
|
||||||
|
*
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async calibratePedal(name) {
|
||||||
|
this.$wizardInstructions.innerHTML = `
|
||||||
|
<p>Waiting for <strong>${name}</strong> activity.</p>
|
||||||
|
`;
|
||||||
|
const { type, index } = await this.detectActivity();
|
||||||
|
if (type === 'button') {
|
||||||
|
this.$wizardInstructions.innerHTML = `
|
||||||
|
<p>Release the <strong>${name}</strong> button.</p>
|
||||||
|
`;
|
||||||
|
await this.waitButtonRelease(index);
|
||||||
|
return { type, index, releasedValue: 0, pressedValue: 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$wizardInstructions.innerHTML = `
|
||||||
|
<p>Press and hold the <strong>${name}</strong> axis.</p>
|
||||||
|
`;
|
||||||
|
const pressedValue = await this.getAxisPush(index);
|
||||||
|
|
||||||
|
this.$wizardInstructions.innerHTML = `
|
||||||
|
<p>Release the <strong>${name}</strong> axis.</p>
|
||||||
|
`;
|
||||||
|
const releasedValue = await this.getAxisPush(index, pressedValue);
|
||||||
|
|
||||||
|
return { type, index, releasedValue, pressedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calibrates the steering axis
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async calibrateSteering() {
|
||||||
|
this.$wizardInstructions.innerHTML = `
|
||||||
|
<p>Turn the <strong>steering</strong> axis all the way to the <strong>left</strong>.</p>
|
||||||
|
`;
|
||||||
|
const { index } = await this.detectActivity('axis', 0.2);
|
||||||
|
const leftValue = await this.getAxisPush(index, 0);
|
||||||
|
|
||||||
|
this.$wizardInstructions.innerHTML = `
|
||||||
|
<p>Turn the <strong>steering</strong> axis all the way to the <strong>right</strong>.</p>
|
||||||
|
`;
|
||||||
|
const rightValue = await this.getAxisPush(index, leftValue);
|
||||||
|
|
||||||
|
return { index, leftValue, rightValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an object to a query string, ignoring empty values
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
toOptionsParams(options) {
|
||||||
|
return Object.entries(options)
|
||||||
|
.filter(([, value]) => value !== undefined && value !== null)
|
||||||
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
|
.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the query string for a pedal
|
||||||
|
*
|
||||||
|
* @param {string} name
|
||||||
|
* @param {Object} data
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
toPedalParams(name, { type, index, releasedValue, pressedValue }) {
|
||||||
|
return `${name}Type=${type}&${name}Index=${index}&${name}Min=${releasedValue}&${name}Max=${pressedValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the query string for the steering
|
||||||
|
*
|
||||||
|
* @param {Object} data
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
toSteeringParams({ index, leftValue, rightValue }) {
|
||||||
|
return `steeringIndex=${index}&steeringMin=${leftValue}&steeringMax=${rightValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the wizard
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
async wizard() {
|
||||||
|
this.$wizard.classList.add('active');
|
||||||
|
|
||||||
|
const gamepad = this.gamepad.getActive();
|
||||||
|
const { withClutch, withBrake, withThrottle, withSteering, chart, history, meters, angle, fps } = await this.askForOptions();
|
||||||
|
const clutch = withClutch && await this.calibratePedal('clutch');
|
||||||
|
const brake = withBrake && await this.calibratePedal('brake');
|
||||||
|
const throttle = withThrottle && await this.calibratePedal('throttle');
|
||||||
|
const steering = withSteering && await this.calibrateSteering();
|
||||||
|
|
||||||
|
window.location.href = [
|
||||||
|
`?gamepad=${gamepad.id}&type=telemetry`,
|
||||||
|
this.toOptionsParams({ chart, history, meters, angle, fps }),
|
||||||
|
withClutch ? this.toPedalParams('clutch', clutch) : null,
|
||||||
|
withBrake ? this.toPedalParams('brake', brake) : null,
|
||||||
|
withThrottle ? this.toPedalParams('throttle', throttle) : null,
|
||||||
|
withSteering ? this.toSteeringParams(steering) : null
|
||||||
|
].filter(e => e !== null).join('&');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
#gamepad {
|
.controller {
|
||||||
height: 630px;
|
height: 630px;
|
||||||
width: 750px;
|
width: 750px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad[data-color="black"] {
|
#gamepad[data-color="black"] .controller {
|
||||||
background-image: url(base-black.svg);
|
background-image: url(base-black.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad[data-color="white"] {
|
#gamepad[data-color="white"] .controller {
|
||||||
background-image: url(base-white.svg);
|
background-image: url(base-white.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad.disconnected {
|
#gamepad.disconnected .controller {
|
||||||
background-image: url(disconnected.svg);
|
background-image: url(disconnected.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,42 +19,42 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .triggers {
|
.controller .triggers {
|
||||||
width: 448px;
|
width: 448px;
|
||||||
height: 122px;
|
height: 122px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 151px;
|
left: 151px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .trigger {
|
.controller .trigger {
|
||||||
width: 89px;
|
width: 89px;
|
||||||
height: 122px;
|
height: 122px;
|
||||||
background: url(trigger.svg);
|
background: url(trigger.svg);
|
||||||
clip-path: inset(100% 0px 0px 0pc);
|
clip-path: inset(100% 0px 0px 0pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .trigger[data-value="0"] {
|
.controller .trigger[data-value="0"] {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .trigger.left {
|
.controller .trigger.left {
|
||||||
float: left;
|
float: left;
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .trigger.right {
|
.controller .trigger.right {
|
||||||
float: right;
|
float: right;
|
||||||
transform: rotateY(180deg);
|
transform: rotateY(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumper {
|
.controller .bumper {
|
||||||
width: 170px;
|
width: 170px;
|
||||||
height: 61px;
|
height: 61px;
|
||||||
background: url(bumper.svg);
|
background: url(bumper.svg);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumpers {
|
.controller .bumpers {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 536px;
|
width: 536px;
|
||||||
height: 61px;
|
height: 61px;
|
||||||
@ -62,41 +62,41 @@
|
|||||||
top: 129px;
|
top: 129px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumper[data-pressed="true"] {
|
.controller .bumper[data-pressed="true"] {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumper.left {
|
.controller .bumper.left {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .bumper.right {
|
.controller .bumper.right {
|
||||||
float: right;
|
float: right;
|
||||||
-webkit-transform: rotateY(180deg);
|
-webkit-transform: rotateY(180deg);
|
||||||
transform: rotateY(180deg);
|
transform: rotateY(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .p0 {
|
.controller .p0 {
|
||||||
-webkit-transform: rotate(0deg);
|
-webkit-transform: rotate(0deg);
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .p1 {
|
.controller .p1 {
|
||||||
-webkit-transform: rotate(90deg);
|
-webkit-transform: rotate(90deg);
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .p2 {
|
.controller .p2 {
|
||||||
-webkit-transform: rotate(270deg);
|
-webkit-transform: rotate(270deg);
|
||||||
transform: rotate(270deg);
|
transform: rotate(270deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .p3 {
|
.controller .p3 {
|
||||||
-webkit-transform: rotate(180deg);
|
-webkit-transform: rotate(180deg);
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .arrows {
|
.controller .arrows {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 141px;
|
width: 141px;
|
||||||
height: 33px;
|
height: 33px;
|
||||||
@ -104,29 +104,29 @@
|
|||||||
left: 306px;
|
left: 306px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .select,
|
.controller .select,
|
||||||
#gamepad .start {
|
.controller .start {
|
||||||
background: url(start-select.svg);
|
background: url(start-select.svg);
|
||||||
width: 33px;
|
width: 33px;
|
||||||
height: 33px;
|
height: 33px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .select[data-pressed="true"],
|
.controller .select[data-pressed="true"],
|
||||||
#gamepad .start[data-pressed="true"] {
|
.controller .start[data-pressed="true"] {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .select {
|
.controller .select {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .start {
|
.controller .start {
|
||||||
background-position: 33px 0px;
|
background-position: 33px 0px;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .buttons {
|
.controller .buttons {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 155px;
|
width: 155px;
|
||||||
height: 156px;
|
height: 156px;
|
||||||
@ -134,43 +134,43 @@
|
|||||||
left: 489px;
|
left: 489px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .button {
|
.controller .button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: url(buttons.svg);
|
background: url(buttons.svg);
|
||||||
width: 53px;
|
width: 53px;
|
||||||
height: 53px;
|
height: 53px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .button[data-pressed="true"] {
|
.controller .button[data-pressed="true"] {
|
||||||
background-position-y: -53px;
|
background-position-y: -53px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .a {
|
.controller .a {
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
top: 102px;
|
top: 102px;
|
||||||
left: 51px;
|
left: 51px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .b {
|
.controller .b {
|
||||||
background-position: -53px 0;
|
background-position: -53px 0;
|
||||||
top: 52px;
|
top: 52px;
|
||||||
right: 1px;
|
right: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .x {
|
.controller .x {
|
||||||
background-position: -106px 0;
|
background-position: -106px 0;
|
||||||
top: 52px;
|
top: 52px;
|
||||||
left: 1px;
|
left: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .y {
|
.controller .y {
|
||||||
background-position: -159px 0;
|
background-position: -159px 0;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
left: 51px;
|
left: 51px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .sticks {
|
.controller .sticks {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 371px;
|
width: 371px;
|
||||||
height: 196px;
|
height: 196px;
|
||||||
@ -178,7 +178,7 @@
|
|||||||
left: 144px;
|
left: 144px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick {
|
.controller .stick {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: url(stick.svg);
|
background: url(stick.svg);
|
||||||
background-position: -85px 0;
|
background-position: -85px 0;
|
||||||
@ -186,21 +186,21 @@
|
|||||||
width: 83px;
|
width: 83px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick[data-pressed="true"] {
|
.controller .stick[data-pressed="true"] {
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick.left {
|
.controller .stick.left {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .stick.right {
|
.controller .stick.right {
|
||||||
top: 113px;
|
top: 113px;
|
||||||
left: 288px;
|
left: 288px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .dpad {
|
.controller .dpad {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 110px;
|
width: 110px;
|
||||||
height: 111px;
|
height: 111px;
|
||||||
@ -208,17 +208,17 @@
|
|||||||
left: 223px;
|
left: 223px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face {
|
.controller .face {
|
||||||
background: url(dpad.svg);
|
background: url(dpad.svg);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face[data-pressed="true"] {
|
.controller .face[data-pressed="true"] {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.up {
|
.controller .face.up {
|
||||||
background-position: 35px 0;
|
background-position: 35px 0;
|
||||||
left: 38px;
|
left: 38px;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
@ -226,14 +226,14 @@
|
|||||||
height: 56px;
|
height: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.down {
|
.controller .face.down {
|
||||||
left: 38px;
|
left: 38px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 34px;
|
width: 34px;
|
||||||
height: 56px;
|
height: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.left {
|
.controller .face.left {
|
||||||
background-position: 0 -93px;
|
background-position: 0 -93px;
|
||||||
width: 56px;
|
width: 56px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
@ -241,7 +241,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad .face.right {
|
.controller .face.right {
|
||||||
background-position: 0 -57px;
|
background-position: 0 -57px;
|
||||||
width: 56px;
|
width: 56px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<div class="controller">
|
||||||
<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>
|
||||||
@ -30,3 +31,4 @@
|
|||||||
<span class="face left" data-button="14"></span>
|
<span class="face left" data-button="14"></span>
|
||||||
<span class="face right" data-button="15"></span>
|
<span class="face right" data-button="15"></span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
window.gamepad.template = class XboxOneTemplate {
|
window.gamepad.templateClass = class XboxOneTemplate {
|
||||||
/**
|
/**
|
||||||
* Instanciates a new Xbox One controller template
|
* Instanciates a new Xbox One controller template
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user