solomonlai.ng/demo-v5-pure-gaussian.js
Solomon Laing 800b2864a6
All checks were successful
continuous-integration/drone/push Build is passing
feat: updated styling and animations
2026-02-13 09:07:38 +10:30

95 lines
3.8 KiB
JavaScript

// Version 5: Pure Gaussian Noise - Infinite organic motion without looping
const staggerVisualizerEl = document.querySelector('.stagger-visualizer');
const fragment = document.createDocumentFragment();
const grid = [12, 6];
const col = grid[0];
const row = grid[1];
const numberOfElements = col * row;
// Store element data for continuous noise
const elementData = [];
for (let i = 0; i < numberOfElements; i++) {
let div = document.createElement('div');
div.className = 'part';
div.dataset.index = i;
fragment.appendChild(div);
// Each element gets unique noise parameters
elementData.push({
element: div,
index: i,
// Multiple frequency layers for natural-looking noise
freq1X: anime.utils.random(0.25, 0.75),
freq2X: anime.utils.random(0.1, 0.25),
freq1Y: anime.utils.random(0.2, 0.6),
freq2Y: anime.utils.random(0.05, 0.2),
freq1Rot: anime.utils.random(0.1, 0.4),
freq2Rot: anime.utils.random(0.025, 0.1),
freqScale: anime.utils.random(0.15, 0.4),
freqOpacity: anime.utils.random(0.05, 0.25),
// Amplitudes
ampX: anime.utils.random(4, 12),
ampY: anime.utils.random(4, 12),
ampRot: anime.utils.random(20, 45),
ampScale: anime.utils.random(0.05, 0.15),
// Phase offsets for variation
phase1X: Math.random() * Math.PI * 2,
phase2X: Math.random() * Math.PI * 2,
phase1Y: Math.random() * Math.PI * 2,
phase2Y: Math.random() * Math.PI * 2,
phase1Rot: Math.random() * Math.PI * 2,
phase2Rot: Math.random() * Math.PI * 2,
phaseScale: Math.random() * Math.PI * 2,
phaseOpacity: Math.random() * Math.PI * 2,
// Random drift direction preference
driftX: anime.utils.random(-0.5, 0.5),
driftY: anime.utils.random(-0.5, 0.5),
});
}
staggerVisualizerEl.appendChild(fragment);
// Continuous Gaussian noise animation using requestAnimationFrame
let animationStart = Date.now();
let lastFrameTime = animationStart;
const updateNoise = () => {
const now = Date.now();
const elapsed = (now - animationStart) / 1000; // Convert to seconds
lastFrameTime = now;
elementData.forEach((data) => {
// Multi-layered sine waves create smooth Gaussian-like noise
// Layer 1: Low frequency, high amplitude (main motion)
const noiseX1 = Math.sin(elapsed * data.freq1X + data.phase1X) * data.ampX;
const noiseY1 = Math.sin(elapsed * data.freq1Y + data.phase1Y) * data.ampY;
const noiseRot1 = Math.sin(elapsed * data.freq1Rot + data.phase1Rot) * data.ampRot;
// Layer 2: Higher frequency, lower amplitude (detail/jitter)
const noiseX2 = Math.sin(elapsed * data.freq2X + data.phase2X) * data.ampX * 0.4;
const noiseY2 = Math.sin(elapsed * data.freq2Y + data.phase2Y) * data.ampY * 0.3;
const noiseRot2 = Math.sin(elapsed * data.freq2Rot + data.phase2Rot) * data.ampRot * 0.3;
// Combine layers
const totalX = noiseX1 + noiseX2 + (data.driftX * elapsed * 0.01);
const totalY = noiseY1 + noiseY2 + (data.driftY * elapsed * 0.01);
const totalRot = noiseRot1 + noiseRot2;
// Scale breathing
const scale = 1 + (Math.sin(elapsed * data.freqScale + data.phaseScale) * data.ampScale);
// Opacity breathing - very subtle
const opacity = 0.7 + Math.sin(elapsed * data.freqOpacity + data.phaseOpacity) * 0.25;
// Apply transforms directly to style
data.element.style.transform = `translateX(${totalX}px) translateY(${totalY}px) rotate(${totalRot}deg) scale(${Math.max(0.3, scale)})`;
data.element.style.opacity = opacity;
});
requestAnimationFrame(updateNoise);
};
// Start the noise animation
updateNoise();