143 lines
4.9 KiB
JavaScript
143 lines
4.9 KiB
JavaScript
// 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 ?
|
|
'<div class="cursor__inner-box"></div>' :
|
|
`<div style="transform: scale(${gsap.utils.random(0.5,2)}); background:${customColorsArr[Math.round(gsap.utils.random(0,customColorsTotal-1))]}" class="cursor__inner-box"></div>`;
|
|
}
|
|
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];
|
|
}
|
|
} |