diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..dfe0770
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..863b6e0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+.cache
+.parcel-cache
+package-lock.json
\ No newline at end of file
diff --git a/css/base.css b/css/base.css
new file mode 100644
index 0000000..17dcb15
--- /dev/null
+++ b/css/base.css
@@ -0,0 +1,373 @@
+*,
+*::after,
+*::before {
+ box-sizing: border-box;
+}
+
+:root {
+ font-size: 14px;
+ --color-text: #000;
+ --color-bg: #f2f1eb;
+ --color-link: #000;
+ --color-link-hover: #000;
+ --page-padding: 1rem;
+ --columns: 15;
+ --cursor-blend-mode: difference;
+ --cursor-radius: 0;
+}
+
+.demo-1 {
+ --color-text: #cdbcbc;
+ --color-bg: #000;
+ --color-link: #fff;
+ --color-link-hover: #fff;
+ --cursor-bg: #5cafc1;
+ --cursor-blend-mode: exclusion;
+ --gradient-text-1: #e8c942;
+ --gradient-text-2: rgb(148 98 209);
+ background-image: url(../img/bg1.jpg);
+}
+
+.demo-2 {
+ --cursor-bg: #47ea82;
+ --cursor-blend-mode: overlay;
+ --gradient-text-1: #85f2c3;
+ --gradient-text-2: rgb(246 99 115);
+ background-image: url(../img/bg2.jpg);
+}
+
+.demo-3 {
+ --color-text: #234acd;
+ --color-bg: #000;
+ --color-link: #6fd74e;
+ --color-link-hover: #fff;
+ --cursor-bg: #db970b;
+ --cursor-blend-mode: overlay;
+ --gradient-text-1: #1728eb;
+ --gradient-text-2: rgb(95 238 56);
+ background-image: url(../img/bg3.jpg);
+}
+
+.demo-4 {
+ --color-text: #fff;
+ --color-bg: #000;
+ --color-link: #9fdee9;
+ --color-link-hover: #fff;
+ --cursor-bg: #f3d006;
+ --cursor-blend-mode: exclusion;
+ --gradient-text-1: #aedfea;
+ --gradient-text-2: rgb(253 235 188);
+ background-image: url(../img/bg4.jpg);
+}
+
+.demo-5 {
+ --cursor-bg: #ea5b2acc;
+ --gradient-text-1: #09314c;
+ --gradient-text-2: rgb(242 133 93);
+ --cursor-blend-mode: none;
+ background-image: url(../img/bg5.jpg);
+}
+
+.demo-6 {
+ --cursor-bg: #c54733;
+ --gradient-text-1: #000;
+ --gradient-text-2: rgb(234 105 79);
+ --cursor-blend-mode: none;
+ background-image: url(../img/bg6.jpg);
+ --cursor-radius: 50%;
+}
+
+.demo-7 {
+ --cursor-bg: #901ee9;
+ --gradient-text-1: #000;
+ --gradient-text-2: rgb(94 85 147);
+ --cursor-radius: 50%;
+ --cursor-blend-mode: color-burn;
+ background-image: url(../img/bg7.jpg);
+}
+
+.demo-8 {
+ --cursor-bg: radial-gradient(#fddc6d 10%,transparent);
+ --gradient-text-1: #000;
+ --gradient-text-2: rgb(94 85 147);
+ background-image: url(../img/bg8.jpg);
+ --cursor-blend-mode: overlay;
+}
+
+body {
+ margin: 0;
+ color: var(--color-text);
+ background-color: var(--color-bg);
+ font-family: "source-code-pro", monospace;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ background-size: cover;
+ background-position: 50% 50%;
+ height: 100vh;
+ overflow: hidden;
+}
+
+/* Page Loader */
+.js .loading::before,
+.js .loading::after {
+ content: '';
+ position: fixed;
+ z-index: 1000;
+}
+
+.js .loading::before {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--color-bg);
+}
+
+.js .loading::after {
+ top: 50%;
+ left: 50%;
+ width: 60px;
+ height: 60px;
+ margin: -30px 0 0 -30px;
+ border-radius: 50%;
+ opacity: 0.4;
+ background: var(--color-link);
+ animation: loaderAnim 0.7s linear infinite alternate forwards;
+
+}
+
+@keyframes loaderAnim {
+ to {
+ opacity: 1;
+ transform: scale3d(0.5,0.5,1);
+ }
+}
+
+a {
+ text-decoration: none;
+ color: var(--color-link);
+ outline: none;
+ cursor: pointer;
+}
+
+a:hover {
+ color: var(--color-link-hover);
+ outline: none;
+}
+
+/* Better focus styles from https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible */
+a:focus {
+ /* Provide a fallback style for browsers
+ that don't support :focus-visible */
+ outline: none;
+ background: lightgrey;
+}
+
+a:focus:not(:focus-visible) {
+ /* Remove the focus indicator on mouse-focus for browsers
+ that do support :focus-visible */
+ background: transparent;
+}
+
+a:focus-visible {
+ /* Draw a very noticeable focus style for
+ keyboard-focus on browsers that do support
+ :focus-visible */
+ outline: 2px solid red;
+ background: transparent;
+}
+
+.unbutton {
+ background: none;
+ border: 0;
+ padding: 0;
+ margin: 0;
+ font: inherit;
+ cursor: pointer;
+}
+
+.unbutton:focus {
+ outline: none;
+}
+
+.frame {
+ position: absolute;
+ width: 100%;
+ padding: var(--page-padding);
+ display: grid;
+ grid-template-columns: 100%;
+ grid-template-areas: 'title' 'prev' 'sponsor' 'demos';
+ grid-gap: 0.5rem;
+ justify-items: start;
+ align-self: start;
+ justify-self: start;
+ pointer-events: none;
+ align-items: center;
+ z-index: 999;
+}
+
+body #cdawrap {
+ justify-self: start;
+}
+
+.frame a {
+ pointer-events: auto;
+}
+
+.frame__title {
+ grid-area: title;
+ display: flex;
+}
+
+.frame__title-main {
+ font-size: inherit;
+ margin: 0;
+ font-weight: inherit;
+}
+
+.frame__title-back {
+ position: relative;
+ display: flex;
+ align-items: flex-end;
+ margin-bottom: 0.15rem;
+}
+
+.frame__title-back span {
+ display: none;
+}
+
+.frame__title-back svg {
+ fill: currentColor;
+}
+
+.frame__prev {
+ grid-area: prev;
+}
+
+.frame__demos {
+ grid-area: demos;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 2rem;
+}
+
+.frame__demos-item:first-child {
+ width: 100%;
+}
+
+a.frame__demos-item {
+ font-weight: bold;
+}
+
+.content {
+ padding: 0 var(--page-padding);
+ display: flex;
+ flex-direction: column;
+ width: 100vw;
+ min-height: 100vh;
+ position: relative;
+ justify-content: center;
+ align-items: start;
+ font-family: "greycliff-cf", sans-serif;
+ font-weight: 300;
+}
+
+.content h2 {
+ font-size: 16vw;
+ line-height: 1;
+ margin: 0;
+ pointer-events: none;
+ background-color: var(--gradient-text-1);
+ background-image: linear-gradient(45deg, var(--gradient-text-1), var(--gradient-text-2));
+ background-size: 100%;
+ background-repeat: repeat;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ -moz-background-clip: text;
+ -moz-text-fill-color: transparent;
+}
+
+.content h2 i {
+ font-family: "lores-9-plus-wide", sans-serif;
+ font-weight: 400;
+ font-style: normal;
+ letter-spacing: -1.75vw;
+ font-size: 14.25vw;
+}
+
+.content p {
+ margin: 3vh 0 0 0;
+ max-width: 30ch;
+ font-weight: 400;
+ font-size: clamp(1.5rem, 2vw, 2rem);
+ font-variation-settings: "wght" 400, "opsz" 72, "ital" 0;
+ pointer-events: none;
+ position: relative; /* Относительное позиционирование для правильного расположения псевдоэлемента */
+ z-index: 1; /* Чтобы текст был поверх фона */
+}
+
+.content p::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: -0.5em;
+ right: -0.5em;
+ bottom: 0;
+ background: var(--gradient-text-2); /* Используем ваш цвет для фона */
+ opacity: 0.8; /* Полупрозрачность */
+ border-radius: 8px; /* Плавные углы */
+ z-index: -1; /* Чтобы фон был под текстом */
+ padding: 0.2em;
+}
+
+.cyrillic {
+ font-family: "Moderustic", sans-serif; /* Укажите любой шрифт для кириллицы */
+ font-weight: 400;
+}
+
+.cursor {
+ height: 100%;
+ position: fixed;
+ width: 100%;
+ left: 0;
+ top: 0;
+ pointer-events: none;
+ z-index: 99;
+ mix-blend-mode: var(--cursor-blend-mode);
+ --size: calc(100vw / var(--columns));
+}
+
+.cursor__inner {
+ display: grid;
+ grid-template-columns: repeat(var(--columns), var(--size));
+}
+
+.cursor__inner-box {
+ width: var(--size);
+ height: var(--size);
+ background: var(--cursor-bg);
+ opacity: 0;
+ border-radius: var(--cursor-radius);
+}
+
+@media screen and (min-width: 53em) {
+ :root {
+ --columns: 30;
+ --page-padding: 2rem;
+ }
+ .frame {
+ grid-template-columns: auto auto auto 1fr;
+ grid-template-areas: 'title prev sponsor demos';
+ justify-items: start;
+ grid-gap: 2rem;
+ }
+ .frame__demos {
+ justify-self: end;
+ }
+ .frame__demos-item:first-child {
+ width: auto;
+ }
+ .content {
+ align-items: center;
+ }
+}
diff --git a/css/fonts/greycliff-cf-opentype-300.font b/css/fonts/greycliff-cf-opentype-300.font
new file mode 100644
index 0000000..77b31e0
Binary files /dev/null and b/css/fonts/greycliff-cf-opentype-300.font differ
diff --git a/css/fonts/greycliff-cf-opentype-700.font b/css/fonts/greycliff-cf-opentype-700.font
new file mode 100644
index 0000000..4d798a8
Binary files /dev/null and b/css/fonts/greycliff-cf-opentype-700.font differ
diff --git a/css/fonts/greycliff-cf-woff-300.font b/css/fonts/greycliff-cf-woff-300.font
new file mode 100644
index 0000000..7550179
Binary files /dev/null and b/css/fonts/greycliff-cf-woff-300.font differ
diff --git a/css/fonts/greycliff-cf-woff-700.font b/css/fonts/greycliff-cf-woff-700.font
new file mode 100644
index 0000000..a1dbed0
Binary files /dev/null and b/css/fonts/greycliff-cf-woff-700.font differ
diff --git a/css/fonts/greycliff-cf-woff2-300.font b/css/fonts/greycliff-cf-woff2-300.font
new file mode 100644
index 0000000..c199b6e
Binary files /dev/null and b/css/fonts/greycliff-cf-woff2-300.font differ
diff --git a/css/fonts/greycliff-cf-woff2-700.font b/css/fonts/greycliff-cf-woff2-700.font
new file mode 100644
index 0000000..453f9fa
Binary files /dev/null and b/css/fonts/greycliff-cf-woff2-700.font differ
diff --git a/css/fonts/lores-9-plus-wide-opentype-400.font b/css/fonts/lores-9-plus-wide-opentype-400.font
new file mode 100644
index 0000000..ff0ad14
Binary files /dev/null and b/css/fonts/lores-9-plus-wide-opentype-400.font differ
diff --git a/css/fonts/lores-9-plus-wide-woff-400.font b/css/fonts/lores-9-plus-wide-woff-400.font
new file mode 100644
index 0000000..3a9fc1c
Binary files /dev/null and b/css/fonts/lores-9-plus-wide-woff-400.font differ
diff --git a/css/fonts/lores-9-plus-wide-woff2-400.font b/css/fonts/lores-9-plus-wide-woff2-400.font
new file mode 100644
index 0000000..d820362
Binary files /dev/null and b/css/fonts/lores-9-plus-wide-woff2-400.font differ
diff --git a/css/fonts/source-code-pro-opentype-300.font b/css/fonts/source-code-pro-opentype-300.font
new file mode 100644
index 0000000..03ab68f
Binary files /dev/null and b/css/fonts/source-code-pro-opentype-300.font differ
diff --git a/css/fonts/source-code-pro-opentype-400.font b/css/fonts/source-code-pro-opentype-400.font
new file mode 100644
index 0000000..419fbb5
Binary files /dev/null and b/css/fonts/source-code-pro-opentype-400.font differ
diff --git a/css/fonts/source-code-pro-woff-300.font b/css/fonts/source-code-pro-woff-300.font
new file mode 100644
index 0000000..b90db67
Binary files /dev/null and b/css/fonts/source-code-pro-woff-300.font differ
diff --git a/css/fonts/source-code-pro-woff-400.font b/css/fonts/source-code-pro-woff-400.font
new file mode 100644
index 0000000..3df96be
Binary files /dev/null and b/css/fonts/source-code-pro-woff-400.font differ
diff --git a/css/fonts/source-code-pro-woff2-300.font b/css/fonts/source-code-pro-woff2-300.font
new file mode 100644
index 0000000..3c4646d
Binary files /dev/null and b/css/fonts/source-code-pro-woff2-300.font differ
diff --git a/css/fonts/source-code-pro-woff2-400.font b/css/fonts/source-code-pro-woff2-400.font
new file mode 100644
index 0000000..17cb6d8
Binary files /dev/null and b/css/fonts/source-code-pro-woff2-400.font differ
diff --git a/css/ftj8drh.css b/css/ftj8drh.css
new file mode 100644
index 0000000..f1d1492
--- /dev/null
+++ b/css/ftj8drh.css
@@ -0,0 +1,87 @@
+/*
+ * The Typekit service used to deliver this font or fonts for use on websites
+ * is provided by Adobe and is subject to these Terms of Use
+ * http://www.adobe.com/products/eulas/tou_typekit. For font license
+ * information, see the list below.
+ *
+ * greycliff-cf:
+ * - http://typekit.com/eulas/00000000000000007735fa47
+ * - http://typekit.com/eulas/00000000000000007735fa4c
+ * lores-9-plus-wide:
+ * - http://typekit.com/eulas/00000000000000007735bf8c
+ * source-code-pro:
+ * - http://typekit.com/eulas/00000000000000007735dc00
+ * - http://typekit.com/eulas/00000000000000007735dc06
+ *
+ * © 2009-2024 Adobe Systems Incorporated. All Rights Reserved.
+ */
+/*{"last_published":"2023-06-06 22:06:27 UTC"}*/
+
+/*@import url("https://p.typekit.net/p.css?s=1&k=ftj8drh&ht=tk&f=17453.17454.28337.49469.49474&a=1494256&app=typekit&e=css");*/
+
+@font-face {
+ font-family: "source-code-pro";
+ src: url("fonts/source-code-pro-woff2-300.font") format("woff2"),
+ url("fonts/source-code-pro-woff-300.font") format("woff"),
+ url("fonts/source-code-pro-opentype-300.font") format("opentype");
+ font-display: auto;
+ font-style: normal;
+ font-weight: 300;
+ font-stretch: normal;
+}
+
+@font-face {
+ font-family: "source-code-pro";
+ src: url("fonts/source-code-pro-woff2-400.font") format("woff2"),
+ url("fonts/source-code-pro-woff-400.font") format("woff"),
+ url("fonts/source-code-pro-opentype-400.font") format("opentype");
+ font-display: auto;
+ font-style: normal;
+ font-weight: 400;
+ font-stretch: normal;
+}
+
+@font-face {
+ font-family: "lores-9-plus-wide";
+ src: url("fonts/lores-9-plus-wide-woff2-400.font") format("woff2"),
+ url("fonts/lores-9-plus-wide-woff-400.font") format("woff"),
+ url("fonts/lores-9-plus-wide-opentype-400.font") format("opentype");
+ font-display: auto;
+ font-style: normal;
+ font-weight: 400;
+ font-stretch: normal;
+}
+
+@font-face {
+ font-family: "greycliff-cf";
+ src: url("fonts/greycliff-cf-woff2-700.font") format("woff2"),
+ url("fonts/greycliff-cf-woff-700.font") format("woff"),
+ url("fonts/greycliff-cf-opentype-700.font") format("opentype");
+ font-display: auto;
+ font-style: normal;
+ font-weight: 700;
+ font-stretch: normal;
+}
+
+@font-face {
+ font-family: "greycliff-cf";
+ src: url("fonts/greycliff-cf-woff2-300.font") format("woff2"),
+ url("fonts/greycliff-cf-woff-300.font") format("woff"),
+ url("fonts/greycliff-cf-opentype-300.font") format("opentype");
+ font-display: auto;
+ font-style: normal;
+ font-weight: 300;
+ font-stretch: normal;
+}
+
+@font-face {
+ font-family: 'Moderustic';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(https://fonts.gstatic.com/s/moderustic/v1/2-c39J9s3o6eLFNHFdXYaOX1UUnf3GLnYjALsQNd7Zehaw.woff2) format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+ }
+.tk-source-code-pro { font-family: "source-code-pro",monospace; }
+.tk-lores-9-plus-wide { font-family: "lores-9-plus-wide",sans-serif; }
+.tk-greycliff-cf { font-family: "greycliff-cf",sans-serif; }
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..ee79f02
Binary files /dev/null and b/favicon.ico differ
diff --git a/img/bg1.jpg b/img/bg1.jpg
new file mode 100644
index 0000000..0cd8c21
Binary files /dev/null and b/img/bg1.jpg differ
diff --git a/img/bg2.jpg b/img/bg2.jpg
new file mode 100644
index 0000000..95d963f
Binary files /dev/null and b/img/bg2.jpg differ
diff --git a/img/bg3.jpg b/img/bg3.jpg
new file mode 100644
index 0000000..a417651
Binary files /dev/null and b/img/bg3.jpg differ
diff --git a/img/bg4.jpg b/img/bg4.jpg
new file mode 100644
index 0000000..47e817a
Binary files /dev/null and b/img/bg4.jpg differ
diff --git a/img/bg5.jpg b/img/bg5.jpg
new file mode 100644
index 0000000..74e1f7e
Binary files /dev/null and b/img/bg5.jpg differ
diff --git a/img/bg6.jpg b/img/bg6.jpg
new file mode 100644
index 0000000..0cd906d
Binary files /dev/null and b/img/bg6.jpg differ
diff --git a/img/bg7.jpg b/img/bg7.jpg
new file mode 100644
index 0000000..0f6509f
Binary files /dev/null and b/img/bg7.jpg differ
diff --git a/img/bg8.jpg b/img/bg8.jpg
new file mode 100644
index 0000000..b7e1bc4
Binary files /dev/null and b/img/bg8.jpg differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..2d79540
--- /dev/null
+++ b/index.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+ hubMC
+
+
+
+
+
+
+
+
+
+
+
+
+
HubMC
+
hubMC* - концептуально новый проект. (*WIP)
& rumine в сердечке
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/cursor.js b/js/cursor.js
new file mode 100644
index 0000000..c26baa2
--- /dev/null
+++ b/js/cursor.js
@@ -0,0 +1,143 @@
+// Import required functions from './utils.js'
+import { getMousePos, getWinSize, isFirefox } from './utils.js';
+
+// Initialize mouse position object
+let mousepos = {x: 0, y: 0};
+
+// Update 'mousepos' with the current mouse position
+const updateMousePos = ev => {
+ mousepos = getMousePos(ev);
+};
+
+// Listen for 'mousemove' and 'pointermove' events and update 'mousepos' accordingly
+window.addEventListener('mousemove', updateMousePos);
+window.addEventListener('pointermove', updateMousePos, { passive: true });
+
+// Initialize window size object
+let winsize = getWinSize();
+
+// Recalculate window size on 'resize' event
+window.addEventListener('resize', ev => {
+ winsize = getWinSize();
+});
+
+// Class representing the Goo cursor
+export class GooCursor {
+ // Initialize DOM and style related properties
+ DOM = {
+ // Main DOM element
+ el: null,
+ // .cursor__inner element
+ inner: null,
+ // cells that get shown on mousemove
+ cells: null,
+ };
+ // Size of one cell (.cursor__inner-box)
+ cellSize;
+ // Number of cell rows
+ rows;
+ // Number of cell columns
+ columns;
+ // Settings
+ settings = {
+ // Time that one cells gets visible before fading out
+ ttl: 0.2
+ };
+
+ constructor(DOM_el) {
+ this.DOM.el = DOM_el;
+
+ // Cells wrapper element that gets the SVG filter applied
+ this.DOM.inner = this.DOM.el.querySelector('.cursor__inner');
+
+ // Too much for firefox...
+ if ( !isFirefox() ) {
+ this.DOM.inner.style.filter = 'url(#gooey)';
+ }
+
+ // ttl from data attr
+ this.settings.ttl = this.DOM.el.getAttribute('data-ttl') || this.settings.ttl;
+
+ // Calculate how many cells to insert into the .cursor__inner element:
+ this.layout();
+
+ // Initialize/Bind some events
+ this.initEvents();
+ }
+
+ /**
+ * Initialize/bind events
+ */
+ initEvents() {
+ // Recalculate and create the .cursor__inner-box elements on 'resize'
+ window.addEventListener('resize', () => this.layout());
+
+ // Show/hide cells on 'mousemove' or 'pointermove' events
+ const handleMove = () => {
+ // Check which cell is being "hovered"
+ const cell = this.getCellAtCursor();
+
+ if (cell === null || this.cachedCell === cell) return;
+ // Cache it
+ this.cachedCell = cell;
+ // Set opacity to 1
+ gsap.set(cell, { opacity: 1 });
+ // Set it back to 0 after a certain delay
+ gsap.set(cell, { opacity: 0, delay: this.settings.ttl });
+ // gsap.to(cell, { duration: 0.3, ease: 'expo', opacity: 0, delay: this.settings.ttl });
+ }
+
+ window.addEventListener('mousemove', handleMove);
+ window.addEventListener('pointermove', handleMove, { passive: true });
+ }
+
+ /**
+ * Calculate and create the .cursor__inner-box elements.
+ * These are the elements that get shown when moving the mouse
+ */
+ layout() {
+ // The number of columns is defined in a CSS variable --columns
+ this.columns = getComputedStyle(this.DOM.el).getPropertyValue('--columns');
+ // Calculate cell size
+ this.cellSize = winsize.width/this.columns;
+ // Calculate number of rows
+ this.rows = Math.ceil(winsize.height/this.cellSize);
+ // Calculate the total amount of cells (rows x columns)
+ this.cellsTotal = this.rows * this.columns;
+ // Insert [this.cellsTotal] cursor__inner-box elements inside the .cursor__inner element
+ let innerStr = '';
+ // Erase contents
+ this.DOM.inner.innerHTML = '';
+
+ const customColorsAttr = this.DOM.el.getAttribute('data-custom-colors');
+ let customColorsArr;
+ let customColorsTotal = 0;
+ if ( customColorsAttr ) {
+ customColorsArr = this.DOM.el.getAttribute('data-custom-colors').split(',');
+ customColorsTotal = customColorsArr ? customColorsArr.length : 0;
+ }
+ for (let i = 0; i < this.cellsTotal; ++i) {
+ innerStr += customColorsTotal === 0 ?
+ '' :
+ ``;
+ }
+ this.DOM.inner.innerHTML = innerStr;
+ this.DOM.cells = this.DOM.inner.children;
+ }
+
+ /**
+ * Gets the cell at the position of the cursor
+ */
+ getCellAtCursor() {
+ const columnIndex = Math.floor(mousepos.x / this.cellSize);
+ const rowIndex = Math.floor(mousepos.y / this.cellSize);
+ const cellIndex = rowIndex * this.columns + columnIndex;
+
+ if ( cellIndex >= this.cellsTotal || cellIndex < 0 ) {
+ console.error('Cell index out of bounds');
+ return null;
+ }
+
+ return this.DOM.cells[cellIndex];
+ }
+}
\ No newline at end of file
diff --git a/js/gsap.min.js b/js/gsap.min.js
new file mode 100644
index 0000000..c1b7a8d
--- /dev/null
+++ b/js/gsap.min.js
@@ -0,0 +1,11 @@
+/*!
+ * GSAP 3.11.5
+ * https://greensock.com
+ *
+ * @license Copyright 2023, GreenSock. All rights reserved.
+ * Subject to the terms at https://greensock.com/standard-license or for Club GreenSock members, the agreement issued with that membership.
+ * @author: Jack Doyle, jack@greensock.com
+ */
+
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).window=t.window||{})}(this,function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function r(t){return"string"==typeof t}function s(t){return"function"==typeof t}function t(t){return"number"==typeof t}function u(t){return void 0===t}function v(t){return"object"==typeof t}function w(t){return!1!==t}function x(){return"undefined"!=typeof window}function y(t){return s(t)||r(t)}function P(t){return(i=yt(t,ot))&&Pe}function Q(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function R(t,e){return!e&&console.warn(t)}function S(t,e){return t&&(ot[t]=e)&&i&&(i[t]=e)||ot}function T(){return 0}function ea(t){var e,r,i=t[0];if(v(i)||s(i)||(t=[t]),!(e=(i._gsap||{}).harness)){for(r=gt.length;r--&&!gt[r].targetTest(i););e=gt[r]}for(r=t.length;r--;)t[r]&&(t[r]._gsap||(t[r]._gsap=new qt(t[r],e)))||t.splice(r,1);return t}function fa(t){return t._gsap||ea(Mt(t))[0]._gsap}function ga(t,e,r){return(r=t[e])&&s(r)?t[e]():u(r)&&t.getAttribute&&t.getAttribute(e)||r}function ha(t,e){return(t=t.split(",")).forEach(e)||t}function ia(t){return Math.round(1e5*t)/1e5||0}function ja(t){return Math.round(1e7*t)/1e7||0}function ka(t,e){var r=e.charAt(0),i=parseFloat(e.substr(2));return t=parseFloat(t),"+"===r?t+i:"-"===r?t-i:"*"===r?t*i:t/i}function la(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++ia;)s=s._prev;return s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t,e}function ya(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function za(t,e){!t.parent||e&&!t.parent.autoRemoveChildren||t.parent.remove(t),t._act=0}function Aa(t,e){if(t&&(!e||e._end>t._dur||e._start<0))for(var r=t;r;)r._dirty=1,r=r.parent;return t}function Ca(t,e,r,i){return t._startAt&&(B?t._startAt.revert(ht):t.vars.immediateRender&&!t.vars.autoRevert||t._startAt.render(e,!0,i))}function Ea(t){return t._repeat?Tt(t._tTime,t=t.duration()+t._rDelay)*t:0}function Ga(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function Ha(t){return t._end=ja(t._start+(t._tDur/Math.abs(t._ts||t._rts||X)||0))}function Ia(t,e){var r=t._dp;return r&&r.smoothChildTiming&&t._ts&&(t._start=ja(r._time-(0X)&&e.render(r,!0)),Aa(t,e)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur(n=Math.abs(n))&&(a=i,o=n);return a}function tb(t){return za(t),t.scrollTrigger&&t.scrollTrigger.kill(!!B),t.progress()<1&&St(t,"onInterrupt"),t}function wb(t){if(x()){var e=(t=!t.name&&t.default||t).name,r=s(t),i=e&&!r&&t.init?function(){this._props=[]}:t,n={init:T,render:fe,add:Qt,kill:_e,modifier:pe,rawVars:0},a={targetTest:0,get:0,getSetter:re,aliases:{},register:0};if(Ft(),t!==i){if(pt[e])return;qa(i,qa(ua(t,n),a)),yt(i.prototype,yt(n,ua(t,a))),pt[i.prop=e]=i,t.targetTest&&(gt.push(i),ft[e]=1),e=("css"===e?"CSS":e.charAt(0).toUpperCase()+e.substr(1))+"Plugin"}S(e,i),t.register&&t.register(Pe,i,ge)}else Ct.push(t)}function zb(t,e,r){return(6*(t+=t<0?1:1>16,e>>8&Pt,e&Pt]:0:Dt.black;if(!p){if(","===e.substr(-1)&&(e=e.substr(0,e.length-1)),Dt[e])p=Dt[e];else if("#"===e.charAt(0)){if(e.length<6&&(e="#"+(n=e.charAt(1))+n+(a=e.charAt(2))+a+(s=e.charAt(3))+s+(5===e.length?e.charAt(4)+e.charAt(4):"")),9===e.length)return[(p=parseInt(e.substr(1,6),16))>>16,p>>8&Pt,p&Pt,parseInt(e.substr(7),16)/255];p=[(e=parseInt(e.substr(1),16))>>16,e>>8&Pt,e&Pt]}else if("hsl"===e.substr(0,3))if(p=d=e.match(tt),r){if(~e.indexOf("="))return p=e.match(et),i&&p.length<4&&(p[3]=1),p}else o=+p[0]%360/360,u=p[1]/100,n=2*(h=p[2]/100)-(a=h<=.5?h*(u+1):h+u-h*u),3=U?u.endTime(!1):t._dur;return r(e)&&(isNaN(e)||e in o)?(a=e.charAt(0),s="%"===e.substr(-1),n=e.indexOf("="),"<"===a||">"===a?(0<=n&&(e=e.replace(/=/,"")),("<"===a?u._start:u.endTime(0<=u._repeat))+(parseFloat(e.substr(1))||0)*(s?(n<0?u:i).totalDuration()/100:1)):n<0?(e in o||(o[e]=h),o[e]):(a=parseFloat(e.charAt(n-1)+e.substr(n+1)),s&&i&&(a=a/100*(Z(i)?i[0]:i).totalDuration()),1=r&&te)return i;i=i._next}else for(i=t._last;i&&i._start>=r;){if("isPause"===i.data&&i._start=n._start)&&n._ts&&h!==n){if(n.parent!==this)return this.render(t,e,r);if(n.render(0=this.totalDuration()||!v&&_)&&(f!==this._start&&Math.abs(l)===Math.abs(this._ts)||this._lock||(!t&&g||!(v===m&&0=i&&(a instanceof Jt?e&&n.push(a):(r&&n.push(a),t&&n.push.apply(n,a.getChildren(!0,e,r)))),a=a._next;return n},e.getById=function getById(t){for(var e=this.getChildren(1,1,1),r=e.length;r--;)if(e[r].vars.id===t)return e[r]},e.remove=function remove(t){return r(t)?this.removeLabel(t):s(t)?this.killTweensOf(t):(ya(this,t),t===this._recent&&(this._recent=this._last),Aa(this))},e.totalTime=function totalTime(t,e){return arguments.length?(this._forcing=1,!this._dp&&this._ts&&(this._start=ja(Rt.time-(0r:!r||s.isActive())&&n.push(s):(i=s.getTweensOf(a,r)).length&&n.push.apply(n,i),s=s._next;return n},e.tweenTo=function tweenTo(t,e){e=e||{};var r,i=this,n=xt(i,t),a=e.startAt,s=e.onStart,o=e.onStartParams,u=e.immediateRender,h=Jt.to(i,qa({ease:e.ease||"none",lazy:!1,immediateRender:!1,time:n,overwrite:"auto",duration:e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale())||X,onStart:function onStart(){if(i.pause(),!r){var t=e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale());h._dur!==t&&Ra(h,t,0,1).render(h._time,!0,!0),r=1}s&&s.apply(h,o||[])}},e));return u?h.render(0):h},e.tweenFromTo=function tweenFromTo(t,e,r){return this.tweenTo(e,qa({startAt:{time:xt(this,t)}},r))},e.recent=function recent(){return this._recent},e.nextLabel=function nextLabel(t){return void 0===t&&(t=this._time),rb(this,xt(this,t))},e.previousLabel=function previousLabel(t){return void 0===t&&(t=this._time),rb(this,xt(this,t),1)},e.currentLabel=function currentLabel(t){return arguments.length?this.seek(t,!0):this.previousLabel(this._time+X)},e.shiftChildren=function shiftChildren(t,e,r){void 0===r&&(r=0);for(var i,n=this._first,a=this.labels;n;)n._start>=r&&(n._start+=t,n._end+=t),n=n._next;if(e)for(i in a)a[i]>=r&&(a[i]+=t);return Aa(this)},e.invalidate=function invalidate(t){var e=this._first;for(this._lock=0;e;)e.invalidate(t),e=e._next;return i.prototype.invalidate.call(this,t)},e.clear=function clear(t){void 0===t&&(t=!0);for(var e,r=this._first;r;)e=r._next,this.remove(r),r=e;return this._dp&&(this._time=this._tTime=this._pTime=0),t&&(this.labels={}),Aa(this)},e.totalDuration=function totalDuration(t){var e,r,i,n=0,a=this,s=a._last,o=U;if(arguments.length)return a.timeScale((a._repeat<0?a.duration():a.totalDuration())/(a.reversed()?-t:t));if(a._dirty){for(i=a.parent;s;)e=s._prev,s._dirty&&s.totalDuration(),o<(r=s._start)&&a._sort&&s._ts&&!a._lock?(a._lock=1,Ka(a,s,r-s._delay,1)._lock=0):o=r,r<0&&s._ts&&(n-=r,(!i&&!a._dp||i&&i.smoothChildTiming)&&(a._start+=r/a._ts,a._time-=r,a._tTime-=r),a.shiftChildren(-r,!1,-Infinity),o=0),s._end>n&&s._ts&&(n=s._end),s=e;Ra(a,a===L&&a._time>n?a._time:n,1,1),a._dirty=0}return a._tDur},Timeline.updateRoot=function updateRoot(t){if(L._ts&&(na(L,Ga(t,L)),f=Rt.frame),Rt.frame>=mt){mt+=V.autoSleep||120;var e=L._first;if((!e||!e._ts)&&V.autoSleep&&Rt._listeners.length<2){for(;e&&!e._ts;)e=e._next;e||Rt.sleep()}}},Timeline}(Ut);qa(Xt.prototype,{_lock:0,_hasPause:0,_forcing:0});function ac(t,e,i,n,a,o){var u,h,l,f;if(pt[t]&&!1!==(u=new pt[t]).init(a,u.rawVars?e[t]:function _processVars(t,e,i,n,a){if(s(t)&&(t=Gt(t,a,e,i,n)),!v(t)||t.style&&t.nodeType||Z(t)||J(t))return r(t)?Gt(t,a,e,i,n):t;var o,u={};for(o in t)u[o]=Gt(t[o],a,e,i,n);return u}(e[t],n,a,o,i),i,n,o)&&(i._pt=h=new ge(i._pt,a,t,0,1,u.render,u,0,u.priority),i!==c))for(l=i._ptLookup[i._targets.indexOf(a)],f=u._props.length;f--;)l[u._props[f]]=h;return u}function gc(t,r,e,i){var n,a,s=r.ease||i||"power1.inOut";if(Z(r))a=e[t]||(e[t]=[]),r.forEach(function(t,e){return a.push({t:e/(r.length-1)*100,v:t,e:s})});else for(n in r)a=e[n]||(e[n]=[]),"ease"===n||a.push({t:parseFloat(t),v:r[n],e:s})}var Nt,Wt,Qt=function _addPropTween(t,e,i,n,a,o,u,h,l,f){s(n)&&(n=n(a||0,t,o));var c,d=t[e],p="get"!==i?i:s(d)?l?t[e.indexOf("set")||!s(t["get"+e.substr(3)])?e:"get"+e.substr(3)](l):t[e]():d,_=s(d)?l?ee:te:Zt;if(r(n)&&(~n.indexOf("random(")&&(n=ob(n)),"="===n.charAt(1)&&(!(c=ka(p,n)+(Ya(p)||0))&&0!==c||(n=c))),!f||p!==n||Wt)return isNaN(p*n)||""===n?(d||e in t||Q(e,n),function _addComplexStringPropTween(t,e,r,i,n,a,s){var o,u,h,l,f,c,d,p,_=new ge(this._pt,t,e,0,1,le,null,n),m=0,g=0;for(_.b=r,_.e=i,r+="",(d=~(i+="").indexOf("random("))&&(i=ob(i)),a&&(a(p=[r,i],t,e),r=p[0],i=p[1]),u=r.match(it)||[];o=it.exec(i);)l=o[0],f=i.substring(m,o.index),h?h=(h+1)%5:"rgba("===f.substr(-5)&&(h=1),l!==u[g++]&&(c=parseFloat(u[g-1])||0,_._pt={_next:_._pt,p:f||1===g?f:",",s:c,c:"="===l.charAt(1)?ka(c,l)-c:parseFloat(l)-c,m:h&&h<4?Math.round:0},m=it.lastIndex);return _.c=m")}),s.duration();else{for(l in u={},x)"ease"===l||"easeEach"===l||gc(l,x[l],u,x.easeEach);for(l in u)for(C=u[l].sort(function(t,e){return t.t-e.t}),o=E=0;o=t._tDur||e<0)&&t.ratio===u&&(u&&za(t,1),r||B||(St(t,u?"onComplete":"onReverseComplete",!0),t._prom&&t._prom()))}else t._zTime||(t._zTime=e)}(this,t,e,r);return this},e.targets=function targets(){return this._targets},e.invalidate=function invalidate(t){return t&&this.vars.runBackwards||(this._startAt=0),this._pt=this._op=this._onUpdate=this._lazy=this.ratio=0,this._ptLookup=[],this.timeline&&this.timeline.invalidate(t),z.prototype.invalidate.call(this,t)},e.resetTo=function resetTo(t,e,r,i){d||Rt.wake(),this._ts||this.play();var n,a=Math.min(this._dur,(this._dp._time-this._start)*this._ts);return this._initted||Kt(this,a),n=this._ease(a/this._dur),function _updatePropTweens(t,e,r,i,n,a,s){var o,u,h,l,f=(t._pt&&t._ptCache||(t._ptCache={}))[e];if(!f)for(f=t._ptCache[e]=[],h=t._ptLookup,l=t._targets.length;l--;){if((o=h[l][e])&&o.d&&o.d._pt)for(o=o.d._pt;o&&o.p!==e&&o.fp!==e;)o=o._next;if(!o)return Wt=1,t.vars[e]="+=0",Kt(t,s),Wt=0,1;f.push(o)}for(l=f.length;l--;)(o=(u=f[l])._pt||u).s=!i&&0!==i||n?o.s+(i||0)+a*o.c:i,o.c=r-o.s,u.e&&(u.e=ia(r)+Ya(u.e)),u.b&&(u.b=o.s+Ya(u.b))}(this,t,e,r,i,n,a)?this.resetTo(t,e,r,i):(Ia(this,0),this.parent||xa(this._dp,this,"_first","_last",this._dp._sort?"_start":0),this.render(0))},e.kill=function kill(t,e){if(void 0===e&&(e="all"),!(t||e&&"all"!==e))return this._lazy=this._pt=0,this.parent?tb(this):this;if(this.timeline){var i=this.timeline.totalDuration();return this.timeline.killTweensOf(t,e,Nt&&!0!==Nt.vars.overwrite)._first||tb(this),this.parent&&i!==this.timeline.totalDuration()&&Ra(this,this._dur*this.timeline._tDur/i,0,1),this}var n,a,s,o,u,h,l,f=this._targets,c=t?Mt(t):f,d=this._ptLookup,p=this._pt;if((!e||"all"===e)&&function _arraysMatch(t,e){for(var r=t.length,i=r===e.length;i&&r--&&t[r]===e[r];);return r<0}(f,c))return"all"===e&&(this._pt=0),tb(this);for(n=this._op=this._op||[],"all"!==e&&(r(e)&&(u={},ha(e,function(t){return u[t]=1}),e=u),e=function _addAliasesToVars(t,e){var r,i,n,a,s=t[0]?fa(t[0]).harness:0,o=s&&s.aliases;if(!o)return e;for(i in r=yt({},e),o)if(i in r)for(n=(a=o[i].split(",")).length;n--;)r[a[n]]=r[i];return r}(f,e)),l=f.length;l--;)if(~c.indexOf(f[l]))for(u in a=d[l],"all"===e?(n[l]=e,o=a,s={}):(s=n[l]=n[l]||{},o=e),o)(h=a&&a[u])&&("kill"in h.d&&!0!==h.d.kill(u)||ya(this,h,"_pt"),delete a[u]),"all"!==s&&(s[u]=1);return this._initted&&!this._pt&&p&&tb(this),this},Tween.to=function to(t,e,r){return new Tween(t,e,r)},Tween.from=function from(t,e){return Va(1,arguments)},Tween.delayedCall=function delayedCall(t,e,r,i){return new Tween(e,0,{immediateRender:!1,lazy:!1,overwrite:!1,delay:t,onComplete:e,onReverseComplete:e,onCompleteParams:r,onReverseCompleteParams:r,callbackScope:i})},Tween.fromTo=function fromTo(t,e,r){return Va(2,arguments)},Tween.set=function set(t,e){return e.duration=0,e.repeatDelay||(e.repeat=0),new Tween(t,e)},Tween.killTweensOf=function killTweensOf(t,e,r){return L.killTweensOf(t,e,r)},Tween}(Ut);qa(Jt.prototype,{_targets:[],_lazy:0,_startAt:0,_op:0,_onInit:0}),ha("staggerTo,staggerFrom,staggerFromTo",function(r){Jt[r]=function(){var t=new Xt,e=kt.call(arguments,0);return e.splice("staggerFromTo"===r?5:4,0,0),t[r].apply(t,e)}});function oc(t,e,r){return t.setAttribute(e,r)}function wc(t,e,r,i){i.mSet(t,e,i.m.call(i.tween,r,i.mt),i)}var Zt=function _setterPlain(t,e,r){return t[e]=r},te=function _setterFunc(t,e,r){return t[e](r)},ee=function _setterFuncWithParam(t,e,r,i){return t[e](i.fp,r)},re=function _getSetter(t,e){return s(t[e])?te:u(t[e])&&t.setAttribute?oc:Zt},se=function _renderPlain(t,e){return e.set(e.t,e.p,Math.round(1e6*(e.s+e.c*t))/1e6,e)},oe=function _renderBoolean(t,e){return e.set(e.t,e.p,!!(e.s+e.c*t),e)},le=function _renderComplexString(t,e){var r=e._pt,i="";if(!t&&e.b)i=e.b;else if(1===t&&e.e)i=e.e;else{for(;r;)i=r.p+(r.m?r.m(r.s+r.c*t):Math.round(1e4*(r.s+r.c*t))/1e4)+i,r=r._next;i+=e.c}e.set(e.t,e.p,i,e)},fe=function _renderPropTweens(t,e){for(var r=e._pt;r;)r.r(t,r.d),r=r._next},pe=function _addPluginModifier(t,e,r,i){for(var n,a=this._pt;a;)n=a._next,a.p===i&&a.modifier(t,e,r),a=n},_e=function _killPropTweensOf(t){for(var e,r,i=this._pt;i;)r=i._next,i.p===t&&!i.op||i.op===t?ya(this,i,"_pt"):i.dep||(e=1),i=r;return!e},me=function _sortPropTweensByPriority(t){for(var e,r,i,n,a=t._pt;a;){for(e=a._next,r=i;r&&r.pr>a.pr;)r=r._next;(a._prev=r?r._prev:n)?a._prev._next=a:i=a,(a._next=r)?r._prev=a:n=a,a=e}t._pt=i},ge=(PropTween.prototype.modifier=function modifier(t,e,r){this.mSet=this.mSet||this.set,this.set=wc,this.m=t,this.mt=r,this.tween=e},PropTween);function PropTween(t,e,r,i,n,a,s,o,u){this.t=e,this.s=i,this.c=n,this.p=r,this.r=a||se,this.d=s||this,this.set=o||Zt,this.pr=u||0,(this._next=t)&&(t._prev=this)}ha(vt+"parent,duration,ease,delay,overwrite,runBackwards,startAt,yoyo,immediateRender,repeat,repeatDelay,data,paused,reversed,lazy,callbackScope,stringFilter,id,yoyoEase,stagger,inherit,repeatRefresh,keyframes,autoRevert,scrollTrigger",function(t){return ft[t]=1}),ot.TweenMax=ot.TweenLite=Jt,ot.TimelineLite=ot.TimelineMax=Xt,L=new Xt({sortChildren:!1,defaults:q,autoRemoveChildren:!0,id:"root",smoothChildTiming:!0}),V.stringFilter=Fb;function Dc(t){return(be[t]||xe).map(function(t){return t()})}function Ec(){var t=Date.now(),o=[];2 {
+ gsap.
+ timeline()
+ .addLabel('start', 0)
+ .to(goo.DOM.cells, {
+ duration: 1,
+ ease: 'power4',
+ opacity: 1,
+ stagger: {
+ from: [...goo.DOM.cells].indexOf(goo.getCellAtCursor()),
+ each: 0.02,
+ grid: [goo.rows,goo.columns]
+ }
+ }, 'start')
+ .to(goo.DOM.cells, {
+ duration: 1,
+ ease: 'power1',
+ opacity: 0,
+ stagger: {
+ from: [...goo.DOM.cells].indexOf(goo.getCellAtCursor()),
+ each: 0.03,
+ grid: [goo.rows,goo.columns]
+ }
+ }, 'start+=0.3')
+});
diff --git a/js/utils.js b/js/utils.js
new file mode 100644
index 0000000..21dc729
--- /dev/null
+++ b/js/utils.js
@@ -0,0 +1,23 @@
+// Returns the mouse position
+const getMousePos = e => {
+ return {
+ x : e.clientX,
+ y : e.clientY
+ };
+};
+
+// Returns the window width and height
+const getWinSize = () => {
+ return {
+ width: window.innerWidth,
+ height: window.innerHeight
+ };
+};
+
+const isFirefox = () => navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
+
+export {
+ getMousePos,
+ getWinSize,
+ isFirefox,
+};
\ No newline at end of file