Merge pull request #2 from eugene-serb/dev

Dev
This commit is contained in:
Eugene Serb 2022-06-17 20:24:27 +03:00 committed by GitHub
commit 1d30321468
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 722 additions and 209 deletions

7
404.md
View File

@ -4,8 +4,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" />
<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="shortcut icon" type="image/x-icon" href="https://eugene-serb.github.io/wavelovers/img/favicon.ico" />
<link rel="stylesheet" type="text/css" href="https://eugene-serb.github.io/wavelovers/css/styles.css" />
<style type="text/css">
div.markdown-body > h1, div.footer {
@ -60,11 +60,10 @@
<section class="banner-container">
<div class="banner">
<h1>404</h1>
<span>File not found. Please, go to the </span><a href="https://eugene-serb.github.io/" target="_self">homepage</a><br />
<span>File not found. Please, go to the </span><a href="https://eugene-serb.github.io/wavelovers/" target="_self">homepage</a><br />
<span>Contact info: </span><a href="https://twitter.com/eugene_serb/" target="_blank">@eugene_serb</a>
</div>
</section>
<script src="https://eugene-serb.github.io/js/scripts.js"></script>
<noscript><div><img src="https://mc.yandex.ru/watch/76610724" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
</body>

View File

@ -1,7 +1,7 @@
# Vibration Master
Vibration Master in Javascript, HTML and CSS.
# Wavelovers
Wavelovers in ***Javascript***, ***HTML*** and ***CSS*** **[[rep](https://github.com/eugene-serb/wavelovers/), [site](https://eugene-serb.github.io/wavelovers/)]**.
This is a browser vibration master that can make a vibration massager for your arms from your gamepad.
This is a Wavelovers that can make a vibration massager from your gamepad.
If you are interested in this or my other projects, or would like to suggest and share ideas with me, or just talk to me, contact me: *[@eugene_serb](https://t.me/eugene_serb)*

518
css/styles.css Normal file
View File

@ -0,0 +1,518 @@
/* ------------------------------ */
/* RESET AND BASE STYLES' TUNE UP */
/* ------------------------------ */
@charset 'UTF-8';
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap');
*, ::after, ::before {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* Simple colors */
--color-white: #FFFFFF;
--color-black: #000000;
--color-milk: #F5F5F5;
--color-coal: #3C3C3C;
/* ---------------------- */
/* Palette of five colors */
--color-a: #E27396;
--color-b: #EA9AB2;
--color-c: #EFCFE3;
--color-d: #EAF2D7;
--color-e: #B3DEE2;
}
@media (prefers-color-scheme: light) {
:root {
--color-background: var(--color-white);
--color-content-background: var(--color-milk);
--color-logo: var(--color-white);
--color-header: var(--color-coal);
--color-text: var(--color-coal);
--color-link: var(--color-b);
--color-link-hover: var(--color-a);
--color-border: var(--color-a);
--color-selection: var(--color-b);
--color-table-header-background: transparent;
--color-table-header-text: transparent;
--color-table-item: transparent;
--color-anotation: var(--color-coal);
--color-pattern-button: var(--color-c);
--color-pattern-text: var(--color-white);
}
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--color-coal);
--color-content-background: var(--color-c);
--color-logo: var(--color-white);
--color-header: var(--color-coal);
--color-text: var(--color-coal);
--color-link: var(--color-b);
--color-link-hover: var(--color-a);
--color-border: var(--color-a);
--color-selection: var(--color-b);
--color-table-header-background: transparent;
--color-table-header-text: transparent;
--color-table-item: transparent;
--color-anotation: var(--color-milk);
--color-pattern-button: var(--color-milk);
--color-pattern-text: var(--color-black);
}
}
html {
font-family: 'Roboto', sans-serif;
direction: ltr;
font-size: 16px;
line-height: 1.382em;
}
body {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100vh;
background: var(--color-background);
}
::selection {
background: var(--color-selection);
}
:focus-visible {
outline: 2px solid var(--color-selection);
}
h1, h2, h3, h4, h5, h6 {
color: var(--color-header);
font-weight: 500;
margin-block-start: 0.382em;
margin-block-end: 0.618em;
line-height: 1.382em;
}
h1 { font-size: 2em; }
h2 { font-size: 1.5em; }
h3 { font-size: 1.17em; }
h4 { font-size: 1em; }
h5 { font-size: 0.83em; }
h6 { font-size: 0.67em; }
big { font-size: larger; }
small { font-size: smaller; }
span, p, article, blockquote {
color: var(--color-text);
font-weight: 400;
}
ul {
padding: 16px;
list-style-type: circle;
}
a {
background-color: transparent;
border-bottom: 2px solid transparent;
font-weight: 500;
color: var(--color-link);
text-decoration: none;
}
a:hover {
border-bottom: 2px solid var(--color-link-hover);
color: var(--color-link-hover);
transition: all 0.5s ease;
text-decoration: none;
}
/* ----- */
/* FORMS */
/* ----- */
hr {
margin-block-start: 0.5em;
margin-block-end: 0.5em;
}
button, input, textarea, select {
padding: 4px 8px;
border: 2px solid var(--color-link);
background: var(--color-milk);
color: var(--color-text);
font-size: 16px;
line-height: 1.382em;
white-space: nowrap;
}
button:hover, input:hover,
textarea:hover, select:hover {
border: 2px solid var(--color-link-hover);
transition: all 0.5s ease;
}
option {
background: var(--color-background);
color: var(--color-text);
font-size: 16px;
line-height: 1.382em;
}
option:hover {
background-color: var(--color-selection);
color: var(--color-white);
}
fieldset {
border: 2px solid var(--color-border-alpha);
border-radius: 4px;
padding: 8px;
}
legend {
padding: 2px 4px;
text-align: left;
}
/* ------------- */
/* SERVICE RULES */
/* ------------- */
.hidden {
display: none !important;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
border: 0;
padding: 0;
white-space: nowrap;
clip-path: inset(100%);
clip: rect(0 0 0 0);
overflow: hidden;
}
.container {
width: 100%;
max-width: 1080px;
margin: 0 auto;
padding-left: 16px;
padding-right: 16px;
}
/* ------ */
/* BANNER */
/* ------ */
.banner-container {
min-height: 100vh;
display: flex;
}
.banner {
margin: auto;
padding: 16px;
}
/* ------------ */
/* ITEMS STYLES */
/* ------------ */
/* ------ */
/* HEADER */
/* ------ */
.header {
border-bottom: 32px solid var(--color-border);
background: var(--color-b);
}
.header-wrapper {
padding-top: 64px;
padding-bottom: 16px;
}
.logo-wrapper {
text-align: center;
}
.logo-wrapper__logo {
font-size: 48px;
font-weight: 500;
color: var(--color-logo);
}
.logo-wrapper__logo::selection {
background-color: var(--color-a);
}
@media only screen and (min-width: 540px) {
.logo-wrapper__logo {
font-size: 64px;
font-weight: 500;
color: var(--color-logo);
}
}
.menu-wrapper {
margin-top: 32px;
align-self: center;
}
.navigation {
padding: 0;
list-style-type: none;
display: flex;
justify-content: space-between;
}
.navigation__item {
font-size: 16px;
text-transform: uppercase;
}
.navigation__item a {
color: var(--color-logo);
}
.navigation__item a:hover {
color: var(--color-logo);
border-color: var(--color-logo);
}
.navigation__item a::selection {
background: var(--color-a);
}
@media only screen and (min-width: 720px) {
.header-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 32px;
}
.logo-wrapper {
grid-column: 1 / 2;
grid-row: 1 / 2;
text-align: left;
}
.menu-wrapper {
margin-top: 0px;
grid-column: 2 / 3;
grid-row: 1 / 2;
align-self: flex-end;
}
}
/* ----------------------------------- */
/* MAIN, PAGE FLOW AND CONTENT SECTION */
/* ----------------------------------- */
.page {
flex-grow: 1;
margin-top: 64px;
margin-bottom: 64px;
}
.content {
margin-bottom: 16px;
padding: 32px;
border-radius: 8px;
background: var(--color-content-background);
}
.content__header {
text-align: center;
}
/* ------ */
/* FOOTER */
/* ------ */
.footer {
border-top: 4px solid var(--color-a);
background: var(--color-background);
}
.footer-wrapper {
padding-top: 32px;
padding-bottom: 32px;
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
gap: 32px;
}
.annotation__text {
color: var(--color-anotation);
}
.created-by {
align-self: end;
}
.created-by > span {
padding-right: 8px;
}
.created-by > a {
font-size: 32px;
}
.created-by > a:hover {
border: none;
}
/* ----------- */
/* WAVE MASTER */
/* ----------- */
.wavelovers {
display: flex;
flex-direction: column;
justify-content: flex-start;
gap: 16px;
}
.message {
text-align: center;
font-size: 24px;
}
.pattern-box {
display: flex;
flex-direction: column;
gap: 32px;
}
.pattern-list {
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
gap: 32px;
}
.pattern-item {
width: 300px;
height: 64px;
padding: 16px;
border-radius: 8px;
background: var(--color-pattern-button);
display: flex;
flex-direction: row;
justify-content: flex-start;
gap: 32px;
align-items: center;
text-align: center;
overflow: hidden;
cursor: default;
}
.pattern-item__selected {
background: var(--color-b);
}
.pattern-item__icon{
font-size: 32px;
}
.pattern-item__name {
font-size: 18px;
white-space: nowrap;
overflow: hidden;
color: var(--color-pattern-text);
}
.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-b);
border-radius: 4px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.list-item__info {
display: flex;
flex-direction: column;
gap: 16px;
}
.list-item_selected .list-item__info span {
color: var(--color-white);
}
.controls-box {
display: flex;
flex-direction: row;
justify-content: space-around;
flex-wrap: wrap;
gap: 32px;
}
.controls-box__item {
display: flex;
flex-direction: column;
text-align: center;
gap: 8px;
}
.controls-box__item > span {
font-size: 24px;
}
.gamepad-button-container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 8px;
}
.gamepad-button {
width: 4ch;
height: 4ch;
border: 1px solid black;
border-radius: 100%;
display: flex;
justify-content: center;
align-items: center;
background: grey;
color: white;
text-align: center;
}
.gamepad-button > span { color: var(--color-white); }
.gamepad-button_a { background: green; }
.gamepad-button_b { background: red; }
.gamepad-button_x { background: blue; }
.gamepad-button_y { background: orange; }

View File

@ -1,77 +0,0 @@
/* -------------- */
/* 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
img/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
img/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

BIN
img/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
img/og.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en-us" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Eugene Serb Vibration Master</title>
<title>Wavelovers</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
@ -9,32 +9,35 @@
<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" />
<link rel="canonical" href="https://eugene-serb.github.io/wavelovers/" />
<link rel="stylesheet" type="text/css" href="css/styles.css" />
<link rel="shortcut icon" type="image/x-icon" href="https://eugene-serb.github.io/wavelovers/img/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="https://eugene-serb.github.io/wavelovers/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="https://eugene-serb.github.io/wavelovers/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="https://eugene-serb.github.io/wavelovers/favicon-16x16.png" />
<link rel="manifest" href="https://eugene-serb.github.io/wavelovers/site.webmanifest" />
<meta name="author" content="Eugene Serb" />
<meta name="copyright" content="Eugene Serb, 2021 2022" />
<meta name="copyright" content="Wavelovers, 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, Massager, Vibrator" />
<meta name="description" content="This is a simple vibration master." />
<meta name="keywords" content="Wavelovers, Wave Lovers, Wavemaster, Wave Master, Vibration Master, Vibration, Massager, Vibrator, Gamepad, Gamepad Vibration, Phone Vibration, Gamepad Tester, Phone Vibration Tester, Vibration Tester, Relax, геймпад, джойстик, вибратор, вибромассажер, вибро, вибромассажёр из геймпада, тестер вибрации геймпада, тестер вибрации телефона" />
<meta name="description" content="Wavelovers. Use your device vibration correctly. Make a massager out of a gamepad." />
<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 property="og:title" content="Wavelovers" />
<meta property="og:site_name" content="Wavelovers" />
<meta property="og:description" content="Wavelovers. Use your device vibration correctly. Make a massager out of a gamepad." />
<meta property="og:url" content="https://eugene-serb.github.io/wavelovers/" />
<meta property="og:image" content="https://eugene-serb.github.io/wavelovers/img/og.png" />
<meta property="vk:image" content="https://eugene-serb.github.io/wavelovers/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="twitter:title" content="Wavelovers" />
<meta name="twitter:description" content="Wavelovers. Use your device vibration correctly. Make a massager out of a gamepad." />
<meta name="twitter:image" content="https://eugene-serb.github.io/wavelovers/img/og.png" />
<meta name="google-site-verification" content="qLQbgnmQEfvprDF8WR6oL_b_Qt0R9kKcIEOfHqWlFm8" />
<meta name="yandex-verification" content="e6e0bff7caaa7ecd" />
@ -44,26 +47,26 @@
<!-- 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());
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-4NB4LGNNLB');
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)
(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");
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
ym(79722217, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
ym(79722217, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
</script>
<!-- /Yandex.Metrika counter -->
@ -72,21 +75,21 @@
<header class="header">
<div class="header-wrapper container">
<div class="logo-wrapper">
<span class="logo-wrapper__logo">Eugene Serb.</span>
<span class="logo-wrapper__logo">Wavelovers</span>
</div>
<nav class="menu-wrapper">
<nav class="menu-wrapper hidden">
<ul class="navigation">
<li class="navigation__item">
<a href="https://eugene-serb.github.io/" target="_self" class="navigation__link">Home</a>
<a href="https://eugene-serb.github.io/wavelovers/" 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>
<a href="https://eugene-serb.github.io/wavelovers/#faq" target="_self" class="navigation__link">FAQ</a>
</li>
<li class="navigation__item">
<a href="https://eugene-serb.github.io/services.html" target="_self" class="navigation__link">Services</a>
<a href="https://eugene-serb.github.io/wavelovers/#buy" target="_self" class="navigation__link">Buy Pro</a>
</li>
<li class="navigation__item">
<a href="https://eugene-serb.github.io/projects.html" target="_self" class="navigation__link">Projects</a>
<a href="https://eugene-serb.github.io/wavelovers/#donate" target="_self" class="navigation__link">Donate</a>
</li>
</ul>
</nav>
@ -94,39 +97,33 @@
</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">
<h1 class="visually-hidden">Wavelovers</h1>
<div class="wavelovers">
<div id="pattern-box" class="content pattern-box">
<div id="pattern-list" class="pattern-list"></div>
</div>
<div id="device-box" class="content">
<div class="message">
<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 id="device-list" class="device-list"></div>
</div>
</section>
</div>
</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/vibration-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/vibration-master/" target="_blank">Improve this page.</a>
<span class="annotation__text">© 2022 Wavelovers. Content licensed under </span><a href="https://eugene-serb.github.io/wavelovers/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/wavelovers/" target="_blank">Improve this page.</a>
</div>
<div class="annotation created-by">
<span class="annotation__text">Created by</span><a href="https://eugene-serb.github.io/" target="_blank">Eugene Serb.</a>
</div>
</div>
</footer>
<script src="js/vibration-master.js"></script>
<script src="js/scripts.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>

View File

@ -5,47 +5,11 @@
'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',
icon: '😌',
pattern: [
{
startDelay: 100,
@ -58,6 +22,7 @@ const __PATTERNS = [
{
name: 'Dotted Strong',
type: 'Simple',
icon: '😉',
pattern: [
{
startDelay: 100,
@ -70,6 +35,7 @@ const __PATTERNS = [
{
name: 'Dotted Max',
type: 'Simple',
icon: '🙃',
pattern: [
{
startDelay: 0,
@ -83,6 +49,7 @@ const __PATTERNS = [
{
name: 'Short Dashed Weak',
type: 'Simple',
icon: '🙂',
pattern: [
{
startDelay: 100,
@ -95,6 +62,7 @@ const __PATTERNS = [
{
name: 'Short Dashed Strong',
type: 'Simple',
icon: '😇',
pattern: [
{
startDelay: 100,
@ -107,6 +75,7 @@ const __PATTERNS = [
{
name: 'Short Dashed Max',
type: 'Simple',
icon: '😊',
pattern: [
{
startDelay: 0,
@ -120,6 +89,7 @@ const __PATTERNS = [
{
name: 'Long Dashed Weak',
type: 'Simple',
icon: '😋',
pattern: [
{
startDelay: 100,
@ -132,6 +102,7 @@ const __PATTERNS = [
{
name: 'Long Dashed Strong',
type: 'Simple',
icon: '😜',
pattern: [
{
startDelay: 100,
@ -144,6 +115,7 @@ const __PATTERNS = [
{
name: 'Long Dashed Max',
type: 'Simple',
icon: '🤪',
pattern: [
{
startDelay: 0,
@ -153,6 +125,46 @@ const __PATTERNS = [
},
],
},
/* Constant, 0s, 1s */
{
name: 'Constant Weak',
type: 'Simple',
icon: '😏',
pattern: [
{
startDelay: 0,
duration: 1000,
weakMagnitude: 1.0,
strongMagnitude: 0.0,
},
],
},
{
name: 'Constant Strong',
type: 'Simple',
icon: '🤩',
pattern: [
{
startDelay: 0,
duration: 1000,
weakMagnitude: 0.0,
strongMagnitude: 1.0,
},
],
},
{
name: 'Constant Max',
type: 'Simple',
icon: '😍',
pattern: [
{
startDelay: 0,
duration: 1000,
weakMagnitude: 1.0,
strongMagnitude: 1.0,
},
],
},
];
class Gamepad {
@ -165,6 +177,7 @@ class Gamepad {
init = () => {
this.id = Date.now();
this.canVibrate = (this.unit.vibrationActuator) ? true : false;
this.isSelected = false;
this.isVibrating = false;
this.isLocked = false;
@ -187,18 +200,11 @@ class Gamepad {
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;
@ -207,19 +213,7 @@ class Gamepad {
};
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>`;
<span>Gamepad #${this.unit.index + 1}. ${this.unit.id}.</span>`;
if (this.isSelected === true) {
this.$list_item.classList.add('list-item_selected');
} else {
@ -282,6 +276,11 @@ class Gamepad {
this.cooldown = Date.now() + 500;
};
};
change = (index) => {
this.index = index;
this.pattern = this.library[this.index].pattern;
};
};
class VibrationMaster {
@ -290,6 +289,8 @@ class VibrationMaster {
};
init = () => {
this.#DOMs();
this.patterns = __PATTERNS;
this.print(this.patterns);
if (!this.checkGamepadSupport()) {
console.log(`This browser does not support of gamepads.`);
@ -302,25 +303,23 @@ class VibrationMaster {
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.$MESSAGE.classList.add('hidden');
this.$DEVICE_LIST.classList.remove('hidden');
this.gamepads.forEach(gamepad => {
gamepad.update();
});
} else {
this.$MESSAGE_BOX.classList.remove('hidden');
this.$DEVICE_BOX.classList.add('hidden');
this.$MESSAGE.classList.remove('hidden');
this.$DEVICE_LIST.classList.add('hidden');
};
};
draw = () => {
@ -333,7 +332,7 @@ class VibrationMaster {
eventHandler = () => {
if (this.gamepads.length > 0) {
this.gamepads.forEach(gamepad => {
if (gamepad.unit.vibrationActuator) {
if (gamepad.canVibrate === true) {
if (gamepad.unit.buttons[2].pressed === true &&
gamepad.unit.buttons[3].pressed === true) {
gamepad.lock();
@ -359,19 +358,76 @@ class VibrationMaster {
};
};
print = (patterns) => {
this.$PATTERN_LIST.innerHTML = '';
patterns.forEach((pattern, index) => {
const $container = document.createElement('div');
const $icon = document.createElement('span');
const $name = document.createElement('span');
$container.classList.add('pattern-item');
$icon.classList.add('pattern-item__icon');
$name.classList.add('pattern-item__name');
$icon.innerHTML = pattern.icon;
$name.innerHTML = pattern.name;
$container.appendChild($icon);
$container.appendChild($name);
$container.addEventListener('click', () => this.change(index));
this.$PATTERN_LIST.appendChild($container);
pattern['container'] = $container;
});
};
change = (index) => {
if (this.gamepads.length > 0) {
this.gamepads.forEach(gamepad => {
this.unselect();
if (gamepad.index === index &&
gamepad.isVibrating === true) {
gamepad.isVibrating = false;
gamepad.reset();
} else {
gamepad.change(index);
gamepad.vibrate();
this.select(index);
};
});
} else {
console.log('No connected devices...');
return;
};
};
unselect = () => {
this.patterns.forEach(pattern => {
pattern['container'].classList.remove('pattern-item__selected');
});
};
select = (index) => {
this.patterns[index]['container'].classList.add('pattern-item__selected');
};
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');
this.$MESSAGE = document.querySelector('#message');
this.$DEVICE_LIST = document.querySelector('#device-list');
this.$PATTERN_BOX = document.querySelector('#pattern-box');
this.$PATTERN_LIST = document.querySelector('#pattern-list');
};
#eventListeners = () => {
window.addEventListener('gamepadconnected', (event) => {
this.gamepads.push(new Gamepad(event.gamepad, this.$DEVICE_LIST, __PATTERNS));
if (this.gamepads.length > 1) {
return;
} else {
this.gamepads.push(new Gamepad(event.gamepad, this.$DEVICE_LIST, this.patterns));
};
});
window.addEventListener('gamepaddisconnected', (event) => {
this.gamepads.forEach((gamepad, index) => {

20
site.webmanifest Normal file
View File

@ -0,0 +1,20 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "https://eugene-serb.github.io/wavelovers/img/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "https://eugene-serb.github.io/wavelovers/img/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}