mirror of
https://github.com/eugene-serb/wavelovers.git
synced 2023-09-09 23:41:16 +03:00
First commit of the prototype of the vibration master program
This commit is contained in:
parent
3289138258
commit
a04786edd8
77
css/vibration-master.css
Normal file
77
css/vibration-master.css
Normal file
@ -0,0 +1,77 @@
|
||||
/* -------------- */
|
||||
/* GAMEPAD MASTER */
|
||||
/* -------------- */
|
||||
|
||||
@charset 'UTF-8';
|
||||
|
||||
.vibration-master {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.message-box {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.device-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.device-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
max-width: 100%;
|
||||
padding: 16px;
|
||||
border: 4px solid var(--color-background);
|
||||
outline: 2px solid var(--color-border-alpha);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 640px) {
|
||||
.list-item {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.list-item_selected {
|
||||
outline: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
.list-item__info {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.list-item__info > div {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.controls-banner {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.controls-banner__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
134
index.html
Normal file
134
index.html
Normal file
@ -0,0 +1,134 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Eugene Serb – Vibration Master</title>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<meta name="robots" content="all" />
|
||||
|
||||
<link rel="canonical" href="https://eugene-serb.github.io/vibration-master/" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="https://eugene-serb.github.io/img/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="https://eugene-serb.github.io/css/styles.css" />
|
||||
<link rel="stylesheet" type="text/css" href="css/vibration-master.css" />
|
||||
|
||||
<meta name="author" content="Eugene Serb" />
|
||||
<meta name="copyright" content="Eugene Serb, 2021 – 2022" />
|
||||
<meta name="publisher-email" content="eugene.serb@gmail.com" />
|
||||
<meta name="publisher-url" content="https://eugene-serb.github.io/" />
|
||||
<meta name="keywords" content="Eugene Serb, eugene-serb, eugene_serb, eugene.serb, Evgeniy Serb, Евгений Серб, Novorossiysk, Новороссийск, contacts, CV, resume, portfolio, repositories, services, coder, developer, software engineer, development, frontend, backend, fullstack, Gamepad, Vibration, Vibration Master" />
|
||||
<meta name="description" content="This is a simple vibration master." />
|
||||
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="Eugene Serb – Vibration Master" />
|
||||
<meta property="og:site_name" content="Eugene Serb – Website" />
|
||||
<meta property="og:description" content="This is a simple vibration master." />
|
||||
<meta property="og:url" content="https://eugene-serb.github.io/" />
|
||||
<meta property="og:image" content="https://eugene-serb.github.io/img/og.png" />
|
||||
<meta property="vk:image" content="https://eugene-serb.github.io/img/og.png" />
|
||||
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:creator" content="@eugene_serb" />
|
||||
<meta name="twitter:title" content="Eugene Serb – Vibration Master" />
|
||||
<meta name="twitter:description" content="This is a simple vibration master." />
|
||||
<meta name="twitter:image" content="https://eugene-serb.github.io/img/og.png" />
|
||||
|
||||
<meta name="google-site-verification" content="qLQbgnmQEfvprDF8WR6oL_b_Qt0R9kKcIEOfHqWlFm8" />
|
||||
<meta name="yandex-verification" content="e6e0bff7caaa7ecd" />
|
||||
<meta name="msvalidate.01" content="6E1771734F083E5366205F06314C3577" />
|
||||
<meta name='wmail-verification' content='46d069b79f9c774ce0bbf55f46aef201' />
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-4NB4LGNNLB"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {dataLayer.push(arguments); }
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-4NB4LGNNLB');
|
||||
</script>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<!-- Yandex.Metrika counter -->
|
||||
<script>
|
||||
(function (m, e, t, r, i, k, a) {
|
||||
m[i] = m[i] || function () { (m[i].a = m[i].a || []).push(arguments) };
|
||||
m[i].l = 1 * new Date(); k = e.createElement(t), a = e.getElementsByTagName(t)[0], k.async = 1, k.src = r, a.parentNode.insertBefore(k, a)
|
||||
})
|
||||
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
|
||||
|
||||
ym(79722217, "init", {
|
||||
clickmap: true,
|
||||
trackLinks: true,
|
||||
accurateTrackBounce: true,
|
||||
webvisor: true
|
||||
});
|
||||
</script>
|
||||
<!-- /Yandex.Metrika counter -->
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="header-wrapper container">
|
||||
<div class="logo-wrapper">
|
||||
<span class="logo-wrapper__logo">Eugene Serb.</span>
|
||||
</div>
|
||||
<nav class="menu-wrapper">
|
||||
<ul class="navigation">
|
||||
<li class="navigation__item">
|
||||
<a href="https://eugene-serb.github.io/" target="_self" class="navigation__link">Home</a>
|
||||
</li>
|
||||
<li class="navigation__item">
|
||||
<a href="https://eugene-serb.github.io/#skills" target="_self" class="navigation__link">Skills</a>
|
||||
</li>
|
||||
<li class="navigation__item">
|
||||
<a href="https://eugene-serb.github.io/services.html" target="_self" class="navigation__link">Services</a>
|
||||
</li>
|
||||
<li class="navigation__item">
|
||||
<a href="https://eugene-serb.github.io/projects.html" target="_self" class="navigation__link">Projects</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="page container">
|
||||
<h1 class="visually-hidden">Eugene Serb – Vibration Master</h1>
|
||||
<section class="post">
|
||||
<h2 class="post__header">Vibration Master</h2>
|
||||
<div class="vibration-master">
|
||||
<div class="content-item content_bordered message-box">
|
||||
<span id="message"></span>
|
||||
</div>
|
||||
<div id="device-box" class="content-item content_bordered device-box">
|
||||
<h3>Device List</h3>
|
||||
<div id="device-list" class="device-list"></div>
|
||||
</div>
|
||||
<section class="content-item content_bordered controls-banner">
|
||||
<h3>Disclaimer</h3>
|
||||
<div class="controls-banner__list">
|
||||
<span>
|
||||
The author of the program code and the publisher is not responsible for the possible consequences of using the program. You use the program at your own risk. Please consult your doctor before using the program.
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="footer-wrapper container">
|
||||
<div class="annotation">
|
||||
<span class="annotation__text">© 2021 – 2022 Eugene Serb. Content licensed under </span><a href="https://eugene-serb.github.io/gamepad-master/LICENSE.md" target="_blank">GNU General Public License v3.0</a><br>
|
||||
<span class="annotation__text">This site is open source. </span><a href="https://github.com/eugene-serb/gamepad-master/" target="_blank">Improve this page.</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="js/vibration-master.js"></script>
|
||||
<script src="https://eugene-serb.github.io/js/scripts.js"></script>
|
||||
<noscript><div><img src="https://mc.yandex.ru/watch/79722217" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
||||
</body>
|
||||
</html>
|
||||
|
392
js/vibration-master.js
Normal file
392
js/vibration-master.js
Normal file
@ -0,0 +1,392 @@
|
||||
/* -------------- */
|
||||
/* GAMEPAD MASTER */
|
||||
/* -------------- */
|
||||
|
||||
'use strict';
|
||||
|
||||
const __PATTERNS = [
|
||||
/* Constant, 0s, 1s */
|
||||
{
|
||||
name: 'Constant Weak',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 0,
|
||||
duration: 1000,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 0.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Constant Strong',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 0,
|
||||
duration: 1000,
|
||||
weakMagnitude: 0.0,
|
||||
strongMagnitude: 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Constant Max',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 0,
|
||||
duration: 1000,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
/* Dotted, 0.1s, 0.1s */
|
||||
{
|
||||
name: 'Dotted Weak',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 100,
|
||||
duration: 100,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 0.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Dotted Strong',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 100,
|
||||
duration: 100,
|
||||
weakMagnitude: 0.0,
|
||||
strongMagnitude: 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Dotted Max',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 0,
|
||||
duration: 100,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
/* Short Dashed, 0.1s, 0.25s */
|
||||
{
|
||||
name: 'Short Dashed Weak',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 100,
|
||||
duration: 250,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 0.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Short Dashed Strong',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 100,
|
||||
duration: 250,
|
||||
weakMagnitude: 0.0,
|
||||
strongMagnitude: 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Short Dashed Max',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 0,
|
||||
duration: 250,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
/* Long Dashed, 0.1s, 0.5s */
|
||||
{
|
||||
name: 'Long Dashed Weak',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 100,
|
||||
duration: 500,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 0.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Long Dashed Strong',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 100,
|
||||
duration: 500,
|
||||
weakMagnitude: 0.0,
|
||||
strongMagnitude: 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Long Dashed Max',
|
||||
type: 'Simple',
|
||||
pattern: [
|
||||
{
|
||||
startDelay: 0,
|
||||
duration: 500,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
class Gamepad {
|
||||
constructor(gamepad, $container, library) {
|
||||
this.unit = gamepad;
|
||||
this.$container = $container;
|
||||
this.library = library;
|
||||
this.init();
|
||||
};
|
||||
|
||||
init = () => {
|
||||
this.id = Date.now();
|
||||
this.isSelected = false;
|
||||
this.isVibrating = false;
|
||||
this.isLocked = false;
|
||||
this.cooldown = 0;
|
||||
this.index = 0;
|
||||
this.pattern = [
|
||||
{
|
||||
startDelay: 0,
|
||||
duration: 1000,
|
||||
weakMagnitude: 1.0,
|
||||
strongMagnitude: 1.0,
|
||||
}
|
||||
];
|
||||
this.generateBox();
|
||||
};
|
||||
|
||||
delete = () => {
|
||||
this.$list_item.parentNode.removeChild(this.$list_item);
|
||||
};
|
||||
generateBox = () => {
|
||||
const $list_item = document.createElement('div');
|
||||
const $info_box = document.createElement('div');
|
||||
/*const $button = document.createElement('button');*/
|
||||
|
||||
$list_item.classList.add('list-item');
|
||||
$info_box.classList.add('list-item__info');
|
||||
/*$button.innerText = 'Select';*/
|
||||
|
||||
/*$button.addEventListener('click', () => {
|
||||
this.isSelected = !this.isSelected;
|
||||
});*/
|
||||
|
||||
$list_item.appendChild($info_box);
|
||||
/*$list_item.appendChild($button);*/
|
||||
this.$container.appendChild($list_item);
|
||||
|
||||
this.$list_item = $list_item;
|
||||
this.$info_box = $info_box;
|
||||
this.draw();
|
||||
};
|
||||
draw = () => {
|
||||
this.$info_box.innerHTML = `
|
||||
<h3>#${this.unit.index + 1}. ${this.unit.id}</h3>
|
||||
<span>Vibration Actuator: ${this.unit.vibrationActuator ? 'Available' : 'missing'}</span>
|
||||
<div>
|
||||
<span>Status: </span>
|
||||
<span>${this.isVibrating ? 'Vibrating' : 'Idle'}</span>
|
||||
<span>A / B</span>
|
||||
<span>Key locked: </span>
|
||||
<span>${this.isLocked ? 'Yes' : 'No'}</span>
|
||||
<span>X + Y</span>
|
||||
<span>Mode: </span>
|
||||
<span>${this.index + 1}. ${this.library[this.index].name}</span>
|
||||
<span>LB / RB</span>
|
||||
</div>`;
|
||||
if (this.isSelected === true) {
|
||||
this.$list_item.classList.add('list-item_selected');
|
||||
} else {
|
||||
this.$list_item.classList.remove('list-item_selected');
|
||||
};
|
||||
};
|
||||
|
||||
update = () => {
|
||||
let gamepads = navigator.getGamepads();
|
||||
this.unit = gamepads[this.unit.index];
|
||||
};
|
||||
|
||||
reset = () => {
|
||||
this.isVibrating = false;
|
||||
this.unit.vibrationActuator.reset();
|
||||
};
|
||||
vibrate = async () => {
|
||||
this.isVibrating = true;
|
||||
this.pattern = this.library[this.index].pattern;
|
||||
|
||||
while (this.isVibrating) {
|
||||
for (let i = 0; i < this.pattern.length; i++) {
|
||||
if (this.isVibrating) {
|
||||
this.unit.vibrationActuator.playEffect('dual-rumble', this.pattern[i]);
|
||||
await this.sleep(this.pattern[i].startDelay + this.pattern[i].duration + 100);
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
sleep = (ms) => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
};
|
||||
previous = () => {
|
||||
if (Date.now() >= this.cooldown) {
|
||||
if (this.index === 0) {
|
||||
this.index = this.library.length - 1;
|
||||
} else {
|
||||
this.index--;
|
||||
};
|
||||
this.pattern = this.library[this.index].pattern;
|
||||
this.cooldown = Date.now() + 500;
|
||||
};
|
||||
};
|
||||
next = () => {
|
||||
if (Date.now() >= this.cooldown) {
|
||||
if (this.index === this.library.length - 1) {
|
||||
this.index = 0;
|
||||
} else {
|
||||
this.index++;
|
||||
};
|
||||
this.pattern = this.library[this.index].pattern;
|
||||
this.cooldown = Date.now() + 500;
|
||||
};
|
||||
};
|
||||
lock = () => {
|
||||
if (Date.now() >= this.cooldown) {
|
||||
this.isLocked = !this.isLocked;
|
||||
this.cooldown = Date.now() + 500;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
class GamepadMaster {
|
||||
constructor() {
|
||||
this.init();
|
||||
};
|
||||
init = () => {
|
||||
this.#DOMs();
|
||||
|
||||
if (!this.checkGamepadSupport()) {
|
||||
console.log(`This browser does not support of gamepads.`);
|
||||
this.$MESSAGE.innerText = `This browser does not support of gamepads.`;
|
||||
return;
|
||||
} else {
|
||||
console.log(`Press any gamepad's button or connect new gamepad.`);
|
||||
this.$MESSAGE.innerText = `Press any gamepad's button or connect new gamepad.`;
|
||||
};
|
||||
|
||||
this.gamepads = [];
|
||||
this.#eventListeners();
|
||||
|
||||
this.interval = setInterval(this.eventLoop, 1);
|
||||
};
|
||||
|
||||
eventLoop = () => {
|
||||
this.update();
|
||||
this.draw();
|
||||
this.eventHandler();
|
||||
};
|
||||
update = () => {
|
||||
if (this.gamepads.length > 0) {
|
||||
this.$MESSAGE_BOX.classList.add('hidden');
|
||||
this.$DEVICE_BOX.classList.remove('hidden');
|
||||
this.gamepads.forEach(gamepad => {
|
||||
gamepad.update();
|
||||
});
|
||||
} else {
|
||||
this.$MESSAGE_BOX.classList.remove('hidden');
|
||||
this.$DEVICE_BOX.classList.add('hidden');
|
||||
};
|
||||
};
|
||||
draw = () => {
|
||||
if (this.gamepads.length > 0) {
|
||||
this.gamepads.forEach(gamepad => {
|
||||
gamepad.draw();
|
||||
});
|
||||
};
|
||||
};
|
||||
eventHandler = () => {
|
||||
if (this.gamepads.length > 0) {
|
||||
this.gamepads.forEach(gamepad => {
|
||||
if (gamepad.unit.vibrationActuator) {
|
||||
if (gamepad.unit.buttons[2].pressed === true &&
|
||||
gamepad.unit.buttons[3].pressed === true) {
|
||||
gamepad.lock();
|
||||
};
|
||||
if (gamepad.isLocked === false) {
|
||||
if (gamepad.unit.buttons[0].pressed === true) {
|
||||
if (gamepad.isVibrating === false) {
|
||||
gamepad.vibrate();
|
||||
};
|
||||
};
|
||||
if (gamepad.unit.buttons[1].pressed === true) {
|
||||
gamepad.reset();
|
||||
};
|
||||
if (gamepad.unit.buttons[4].pressed === true) {
|
||||
gamepad.previous();
|
||||
};
|
||||
if (gamepad.unit.buttons[5].pressed === true) {
|
||||
gamepad.next();
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
checkGamepadSupport = () => {
|
||||
return 'getGamepads' in window.navigator;
|
||||
};
|
||||
|
||||
#DOMs = () => {
|
||||
this.$MESSAGE = document.querySelector('#message');
|
||||
this.$MESSAGE_BOX = document.querySelector('.message-box');
|
||||
this.$DEVICE_LIST = document.querySelector('#device-list');
|
||||
this.$DEVICE_BOX = document.querySelector('#device-box');
|
||||
};
|
||||
#eventListeners = () => {
|
||||
window.addEventListener('gamepadconnected', (event) => {
|
||||
this.gamepads.push(new Gamepad(event.gamepad, this.$DEVICE_LIST, __PATTERNS));
|
||||
});
|
||||
window.addEventListener('gamepaddisconnected', (event) => {
|
||||
this.gamepads.forEach((gamepad, index) => {
|
||||
if (gamepad.unit.id === event.gamepad.id) {
|
||||
this.gamepads[index].delete();
|
||||
this.gamepads.splice(index, 1);
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/* -------------- */
|
||||
/* INITIALIZATION */
|
||||
/* -------------- */
|
||||
|
||||
const GAMEPAD_MASTER = new GamepadMaster();
|
||||
|
Loading…
Reference in New Issue
Block a user