eweng.space/assets/swirl.js
2026-05-10 20:37:58 +02:00

125 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
const CHARS = ['~', '-', '_', '·', '°', '*', 'o', '+', '×', '=', '≈', '∿', '◦'];
function pick(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
class Swirl {
constructor(container) {
this.container = container;
this.cx = container.offsetWidth / 2 || window.innerWidth / 2;
this.cy = container.offsetHeight / 2 || window.innerHeight / 2;
this.elements = [];
this.frameCount = 0;
this.angle = 0;
this.radius = 2;
this.hue = Math.random() * 360;
this.hueSpeed = 0.25;
this.angleStep = 0.18;
this.radiusStep = 0.55;
this.globalRotation = 0;
this.loop = this.loop.bind(this);
requestAnimationFrame(this.loop);
window.addEventListener('resize', () => {
this.cx = container.offsetWidth / 2 || window.innerWidth / 2;
this.cy = container.offsetHeight / 2 || window.innerHeight / 2;
});
}
createChar(x, y, hue, scale) {
const color = `hsl(${hue}, 60%, 68%)`;
const el = document.createElement('span');
el.textContent = pick(CHARS);
el.classList.add('swirl-char');
el.style.cssText =
`position:absolute;` +
`left:${x}px;top:${y}px;` +
`font-family:"Courier New",Courier,monospace;` +
`font-size:15px;line-height:1;` +
`color:${color};` +
`pointer-events:none;user-select:none;` +
`opacity:0.85;`;
el.style.transform = `scale(${scale}) rotate(${Math.random() * 360}deg)`;
el.style.display = 'inline-block';
this.container.appendChild(el);
this.elements.push({
el,
born: this.frameCount,
maxAge: 900 + Math.floor(Math.random() * 400),
});
}
ageElements() {
if (this.frameCount % 12 !== 0) return;
const now = this.frameCount;
let pruneCount = 0;
const maxPrune = 12;
for (let i = 0; i < this.elements.length && pruneCount < maxPrune; i++) {
const item = this.elements[i];
const age = now - item.born;
if (age >= item.maxAge) {
item.el.remove();
this.elements.splice(i, 1);
i--;
pruneCount++;
} else if (age > item.maxAge - 150) {
const fade = 1 - (age - (item.maxAge - 150)) / 150;
item.el.style.opacity = Math.max(0, fade * 0.85);
}
}
}
update() {
this.frameCount++;
// Rotation globale du tourbillon
this.globalRotation += 0.002;
// Évolution de la couleur
this.hue = (this.hue + this.hueSpeed) % 360;
// Ajouter un nouveau caractère à la spirale
if (this.frameCount % 2 === 0) {
this.angle += this.angleStep;
// L'espacement augmente avec le rayon (spirale qui s'écarte)
const dynamicStep = this.radiusStep + (this.radius / 100) * 0.18;
this.radius += dynamicStep;
const effectiveAngle = this.angle + this.globalRotation;
const x = this.cx + Math.cos(effectiveAngle) * this.radius;
const y = this.cy + Math.sin(effectiveAngle) * this.radius * 0.6;
// Scale qui grandit avec le rayon
const scale = 0.6 + (this.radius / 600) * 0.6;
this.createChar(x, y, this.hue, scale);
}
// Quand le rayon dépasse l'écran, on recommence plus petit
const maxRadius = Math.max(this.cx, this.cy) + 80;
if (this.radius > maxRadius) {
this.radius = 8 + Math.random() * 12;
this.hue = (this.hue + 60 + Math.random() * 120) % 360;
}
this.ageElements();
}
loop() {
this.update();
requestAnimationFrame(this.loop);
}
}
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('swirl-container');
if (container) {
new Swirl(container);
}
});
})();