7
404.md
@ -4,8 +4,8 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="color-scheme" content="light dark" />
|
<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="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/css/styles.css" />
|
<link rel="stylesheet" type="text/css" href="https://eugene-serb.github.io/wavelovers/css/styles.css" />
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
div.markdown-body > h1, div.footer {
|
div.markdown-body > h1, div.footer {
|
||||||
@ -60,11 +60,10 @@
|
|||||||
<section class="banner-container">
|
<section class="banner-container">
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
<h1>404</h1>
|
<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>
|
<span>Contact info: </span><a href="https://twitter.com/eugene_serb/" target="_blank">@eugene_serb</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script src="https://eugene-serb.github.io/js/scripts.js"></script>
|
<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>
|
<noscript><div><img src="https://mc.yandex.ru/watch/76610724" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Vibration Master
|
# Wavelovers
|
||||||
Vibration Master in Javascript, HTML and CSS.
|
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)*
|
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
@ -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; }
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
BIN
img/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
img/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
img/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
img/favicon-16x16.png
Normal file
After Width: | Height: | Size: 602 B |
BIN
img/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
img/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
img/og.png
Normal file
After Width: | Height: | Size: 18 KiB |
83
index.html
@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en-us" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
|
<html lang="en-us" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<title>Eugene Serb – Vibration Master</title>
|
<title>Wavelovers</title>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
@ -9,32 +9,35 @@
|
|||||||
<meta name="color-scheme" content="light dark" />
|
<meta name="color-scheme" content="light dark" />
|
||||||
<meta name="robots" content="all" />
|
<meta name="robots" content="all" />
|
||||||
|
|
||||||
<link rel="canonical" href="https://eugene-serb.github.io/vibration-master/" />
|
<link rel="canonical" href="https://eugene-serb.github.io/wavelovers/" />
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="https://eugene-serb.github.io/img/favicon.ico" />
|
<link rel="stylesheet" type="text/css" href="css/styles.css" />
|
||||||
<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="css/vibration-master.css" />
|
<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="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-email" content="eugene.serb@gmail.com" />
|
||||||
<meta name="publisher-url" content="https://eugene-serb.github.io/" />
|
<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="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="This is a simple vibration master." />
|
<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:locale" content="en_US" />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:title" content="Eugene Serb – Vibration Master" />
|
<meta property="og:title" content="Wavelovers" />
|
||||||
<meta property="og:site_name" content="Eugene Serb – Website" />
|
<meta property="og:site_name" content="Wavelovers" />
|
||||||
<meta property="og:description" content="This is a simple vibration master." />
|
<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/" />
|
<meta property="og:url" content="https://eugene-serb.github.io/wavelovers/" />
|
||||||
<meta property="og:image" content="https://eugene-serb.github.io/img/og.png" />
|
<meta property="og:image" content="https://eugene-serb.github.io/wavelovers/img/og.png" />
|
||||||
<meta property="vk:image" content="https://eugene-serb.github.io/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:card" content="summary" />
|
||||||
<meta name="twitter:creator" content="@eugene_serb" />
|
<meta name="twitter:creator" content="@eugene_serb" />
|
||||||
<meta name="twitter:title" content="Eugene Serb – Vibration Master" />
|
<meta name="twitter:title" content="Wavelovers" />
|
||||||
<meta name="twitter:description" content="This is a simple vibration master." />
|
<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/img/og.png" />
|
<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="google-site-verification" content="qLQbgnmQEfvprDF8WR6oL_b_Qt0R9kKcIEOfHqWlFm8" />
|
||||||
<meta name="yandex-verification" content="e6e0bff7caaa7ecd" />
|
<meta name="yandex-verification" content="e6e0bff7caaa7ecd" />
|
||||||
@ -45,7 +48,7 @@
|
|||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-4NB4LGNNLB"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-4NB4LGNNLB"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag() {dataLayer.push(arguments); }
|
function gtag() { dataLayer.push(arguments); }
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', 'G-4NB4LGNNLB');
|
gtag('config', 'G-4NB4LGNNLB');
|
||||||
@ -72,21 +75,21 @@
|
|||||||
<header class="header">
|
<header class="header">
|
||||||
<div class="header-wrapper container">
|
<div class="header-wrapper container">
|
||||||
<div class="logo-wrapper">
|
<div class="logo-wrapper">
|
||||||
<span class="logo-wrapper__logo">Eugene Serb.</span>
|
<span class="logo-wrapper__logo">Wavelovers</span>
|
||||||
</div>
|
</div>
|
||||||
<nav class="menu-wrapper">
|
<nav class="menu-wrapper hidden">
|
||||||
<ul class="navigation">
|
<ul class="navigation">
|
||||||
<li class="navigation__item">
|
<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>
|
||||||
<li class="navigation__item">
|
<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>
|
||||||
<li class="navigation__item">
|
<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>
|
||||||
<li class="navigation__item">
|
<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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
@ -94,39 +97,33 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="page container">
|
<main class="page container">
|
||||||
<h1 class="visually-hidden">Eugene Serb – Vibration Master</h1>
|
<h1 class="visually-hidden">Wavelovers</h1>
|
||||||
<section class="post">
|
<div class="wavelovers">
|
||||||
<h2 class="post__header">Vibration Master</h2>
|
<div id="pattern-box" class="content pattern-box">
|
||||||
<div class="vibration-master">
|
<div id="pattern-list" class="pattern-list"></div>
|
||||||
<div class="content-item content_bordered message-box">
|
</div>
|
||||||
|
<div id="device-box" class="content">
|
||||||
|
<div class="message">
|
||||||
<span id="message"></span>
|
<span id="message"></span>
|
||||||
</div>
|
</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 id="device-list" class="device-list"></div>
|
||||||
</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>
|
</div>
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="footer-wrapper container">
|
<div class="footer-wrapper container">
|
||||||
<div class="annotation">
|
<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">© 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/vibration-master/" target="_blank">Improve this page.</a>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</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>
|
<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>
|
<noscript><div><img src="https://mc.yandex.ru/watch/79722217" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
||||||
</body>
|
</body>
|
||||||
|
@ -5,47 +5,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const __PATTERNS = [
|
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 */
|
/* Dotted, 0.1s, 0.1s */
|
||||||
{
|
{
|
||||||
name: 'Dotted Weak',
|
name: 'Dotted Weak',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '😌',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 100,
|
startDelay: 100,
|
||||||
@ -58,6 +22,7 @@ const __PATTERNS = [
|
|||||||
{
|
{
|
||||||
name: 'Dotted Strong',
|
name: 'Dotted Strong',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '😉',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 100,
|
startDelay: 100,
|
||||||
@ -70,6 +35,7 @@ const __PATTERNS = [
|
|||||||
{
|
{
|
||||||
name: 'Dotted Max',
|
name: 'Dotted Max',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '🙃',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 0,
|
startDelay: 0,
|
||||||
@ -83,6 +49,7 @@ const __PATTERNS = [
|
|||||||
{
|
{
|
||||||
name: 'Short Dashed Weak',
|
name: 'Short Dashed Weak',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '🙂',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 100,
|
startDelay: 100,
|
||||||
@ -95,6 +62,7 @@ const __PATTERNS = [
|
|||||||
{
|
{
|
||||||
name: 'Short Dashed Strong',
|
name: 'Short Dashed Strong',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '😇',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 100,
|
startDelay: 100,
|
||||||
@ -107,6 +75,7 @@ const __PATTERNS = [
|
|||||||
{
|
{
|
||||||
name: 'Short Dashed Max',
|
name: 'Short Dashed Max',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '😊',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 0,
|
startDelay: 0,
|
||||||
@ -120,6 +89,7 @@ const __PATTERNS = [
|
|||||||
{
|
{
|
||||||
name: 'Long Dashed Weak',
|
name: 'Long Dashed Weak',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '😋',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 100,
|
startDelay: 100,
|
||||||
@ -132,6 +102,7 @@ const __PATTERNS = [
|
|||||||
{
|
{
|
||||||
name: 'Long Dashed Strong',
|
name: 'Long Dashed Strong',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '😜',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 100,
|
startDelay: 100,
|
||||||
@ -144,6 +115,7 @@ const __PATTERNS = [
|
|||||||
{
|
{
|
||||||
name: 'Long Dashed Max',
|
name: 'Long Dashed Max',
|
||||||
type: 'Simple',
|
type: 'Simple',
|
||||||
|
icon: '🤪',
|
||||||
pattern: [
|
pattern: [
|
||||||
{
|
{
|
||||||
startDelay: 0,
|
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 {
|
class Gamepad {
|
||||||
@ -165,6 +177,7 @@ class Gamepad {
|
|||||||
|
|
||||||
init = () => {
|
init = () => {
|
||||||
this.id = Date.now();
|
this.id = Date.now();
|
||||||
|
this.canVibrate = (this.unit.vibrationActuator) ? true : false;
|
||||||
this.isSelected = false;
|
this.isSelected = false;
|
||||||
this.isVibrating = false;
|
this.isVibrating = false;
|
||||||
this.isLocked = false;
|
this.isLocked = false;
|
||||||
@ -187,18 +200,11 @@ class Gamepad {
|
|||||||
generateBox = () => {
|
generateBox = () => {
|
||||||
const $list_item = document.createElement('div');
|
const $list_item = document.createElement('div');
|
||||||
const $info_box = document.createElement('div');
|
const $info_box = document.createElement('div');
|
||||||
/*const $button = document.createElement('button');*/
|
|
||||||
|
|
||||||
$list_item.classList.add('list-item');
|
$list_item.classList.add('list-item');
|
||||||
$info_box.classList.add('list-item__info');
|
$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($info_box);
|
||||||
/*$list_item.appendChild($button);*/
|
|
||||||
this.$container.appendChild($list_item);
|
this.$container.appendChild($list_item);
|
||||||
|
|
||||||
this.$list_item = $list_item;
|
this.$list_item = $list_item;
|
||||||
@ -207,19 +213,7 @@ class Gamepad {
|
|||||||
};
|
};
|
||||||
draw = () => {
|
draw = () => {
|
||||||
this.$info_box.innerHTML = `
|
this.$info_box.innerHTML = `
|
||||||
<h3>#${this.unit.index + 1}. ${this.unit.id}</h3>
|
<span>Gamepad #${this.unit.index + 1}. ${this.unit.id}.</span>`;
|
||||||
<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) {
|
if (this.isSelected === true) {
|
||||||
this.$list_item.classList.add('list-item_selected');
|
this.$list_item.classList.add('list-item_selected');
|
||||||
} else {
|
} else {
|
||||||
@ -282,6 +276,11 @@ class Gamepad {
|
|||||||
this.cooldown = Date.now() + 500;
|
this.cooldown = Date.now() + 500;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
change = (index) => {
|
||||||
|
this.index = index;
|
||||||
|
this.pattern = this.library[this.index].pattern;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class VibrationMaster {
|
class VibrationMaster {
|
||||||
@ -290,6 +289,8 @@ class VibrationMaster {
|
|||||||
};
|
};
|
||||||
init = () => {
|
init = () => {
|
||||||
this.#DOMs();
|
this.#DOMs();
|
||||||
|
this.patterns = __PATTERNS;
|
||||||
|
this.print(this.patterns);
|
||||||
|
|
||||||
if (!this.checkGamepadSupport()) {
|
if (!this.checkGamepadSupport()) {
|
||||||
console.log(`This browser does not support of gamepads.`);
|
console.log(`This browser does not support of gamepads.`);
|
||||||
@ -302,25 +303,23 @@ class VibrationMaster {
|
|||||||
|
|
||||||
this.gamepads = [];
|
this.gamepads = [];
|
||||||
this.#eventListeners();
|
this.#eventListeners();
|
||||||
|
|
||||||
this.interval = setInterval(this.eventLoop, 1);
|
this.interval = setInterval(this.eventLoop, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
eventLoop = () => {
|
eventLoop = () => {
|
||||||
this.update();
|
this.update();
|
||||||
this.draw();
|
this.draw();
|
||||||
this.eventHandler();
|
|
||||||
};
|
};
|
||||||
update = () => {
|
update = () => {
|
||||||
if (this.gamepads.length > 0) {
|
if (this.gamepads.length > 0) {
|
||||||
this.$MESSAGE_BOX.classList.add('hidden');
|
this.$MESSAGE.classList.add('hidden');
|
||||||
this.$DEVICE_BOX.classList.remove('hidden');
|
this.$DEVICE_LIST.classList.remove('hidden');
|
||||||
this.gamepads.forEach(gamepad => {
|
this.gamepads.forEach(gamepad => {
|
||||||
gamepad.update();
|
gamepad.update();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$MESSAGE_BOX.classList.remove('hidden');
|
this.$MESSAGE.classList.remove('hidden');
|
||||||
this.$DEVICE_BOX.classList.add('hidden');
|
this.$DEVICE_LIST.classList.add('hidden');
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
draw = () => {
|
draw = () => {
|
||||||
@ -333,7 +332,7 @@ class VibrationMaster {
|
|||||||
eventHandler = () => {
|
eventHandler = () => {
|
||||||
if (this.gamepads.length > 0) {
|
if (this.gamepads.length > 0) {
|
||||||
this.gamepads.forEach(gamepad => {
|
this.gamepads.forEach(gamepad => {
|
||||||
if (gamepad.unit.vibrationActuator) {
|
if (gamepad.canVibrate === true) {
|
||||||
if (gamepad.unit.buttons[2].pressed === true &&
|
if (gamepad.unit.buttons[2].pressed === true &&
|
||||||
gamepad.unit.buttons[3].pressed === true) {
|
gamepad.unit.buttons[3].pressed === true) {
|
||||||
gamepad.lock();
|
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 = () => {
|
checkGamepadSupport = () => {
|
||||||
return 'getGamepads' in window.navigator;
|
return 'getGamepads' in window.navigator;
|
||||||
};
|
};
|
||||||
|
|
||||||
#DOMs = () => {
|
#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.$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 = () => {
|
#eventListeners = () => {
|
||||||
window.addEventListener('gamepadconnected', (event) => {
|
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) => {
|
window.addEventListener('gamepaddisconnected', (event) => {
|
||||||
this.gamepads.forEach((gamepad, index) => {
|
this.gamepads.forEach((gamepad, index) => {
|
20
site.webmanifest
Normal 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"
|
||||||
|
}
|
||||||
|
|