Files
NexStudio/public/particle-bg.js
Javaxman b687bc8ac2 Riduce i micro-blocchi allo scroll alleggerendo canvas e spine SVG.
Il canvas particelle era alto quanto tutto il documento e ridisegnava ogni frame; ora resta sul viewport e si ferma durante lo scroll.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-30 11:59:58 +02:00

192 lines
4.7 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.

/**
* Rete di particelle + linee (canvas #particleCanvas).
* Limitato al viewport (non allaltezza documento): evita canvas giganti che
* bloccano lo scroll sulla home lunga.
*/
(function () {
if (typeof window === 'undefined') return;
var canvas = document.getElementById('particleCanvas');
if (!canvas) return;
var ctx = canvas.getContext('2d', { alpha: true });
if (!ctx) return;
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var particles = [];
var mouse = { x: null, y: null, radius: 150 };
var raf = 0;
var running = false;
var cachedW = 0;
var cachedH = 0;
var scrollPauseTimer = 0;
function viewportSize() {
var w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth || 1));
var h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight || 1));
return { w: w, h: h };
}
function Particle(w, h) {
this.x = Math.random() * w;
this.y = Math.random() * h;
this.size = Math.random() * 2 + 1;
this.speedX = (Math.random() - 0.5) * 1.2;
this.speedY = (Math.random() - 0.5) * 1.2;
}
Particle.prototype.draw = function () {
ctx.fillStyle = 'rgba(148, 163, 184, 0.85)';
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
};
Particle.prototype.update = function (w, h) {
this.x += this.speedX;
this.y += this.speedY;
if (this.x > w || this.x < 0) this.speedX *= -1;
if (this.y > h || this.y < 0) this.speedY *= -1;
if (mouse.x == null || mouse.y == null) return;
var dx = mouse.x - this.x;
var dy = mouse.y - this.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 1 && distance < mouse.radius) {
var fx = dx / distance;
var fy = dy / distance;
var force = (mouse.radius - distance) / mouse.radius;
this.x -= fx * force * 4;
this.y -= fy * force * 4;
}
};
function init() {
var sz = viewportSize();
var w = sz.w;
var h = sz.h;
cachedW = w;
cachedH = h;
var dpr = Math.min(window.devicePixelRatio || 1, 1.5);
canvas.width = Math.floor(w * dpr);
canvas.height = Math.floor(h * dpr);
canvas.style.width = w + 'px';
canvas.style.height = h + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
particles = [];
var area = w * h;
var n = Math.floor(area / 14000);
if (n > 80) n = 80;
if (n < 28) n = 28;
if (w < 768) {
n = Math.min(n, 40);
}
for (var i = 0; i < n; i++) {
particles.push(new Particle(w, h));
}
}
function connect(w) {
var maxDist = w < 768 ? 110 : 140;
var a;
var b;
var dx;
var dy;
var distance;
var opacity;
var len = particles.length;
for (a = 0; a < len; a++) {
for (b = a + 1; b < len; b++) {
dx = particles[a].x - particles[b].x;
dy = particles[a].y - particles[b].y;
distance = Math.sqrt(dx * dx + dy * dy);
if (distance < maxDist) {
opacity = (1 - distance / maxDist) * 0.2;
ctx.strokeStyle = 'rgba(59, 130, 246,' + opacity + ')';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(particles[a].x, particles[a].y);
ctx.lineTo(particles[b].x, particles[b].y);
ctx.stroke();
}
}
}
}
function frame() {
if (!running) return;
var w = cachedW;
var h = cachedH;
ctx.clearRect(0, 0, w, h);
var i;
for (i = 0; i < particles.length; i++) {
particles[i].update(w, h);
particles[i].draw();
}
connect(w);
raf = requestAnimationFrame(frame);
}
function start() {
if (reduced || document.hidden || running || !particles.length) return;
running = true;
raf = requestAnimationFrame(frame);
}
function stop() {
running = false;
if (raf) {
cancelAnimationFrame(raf);
raf = 0;
}
}
function onResize() {
stop();
init();
start();
}
function setMouseFromEvent(e) {
mouse.x = e.clientX;
mouse.y = e.clientY;
}
window.addEventListener(
'mousemove',
function (e) {
setMouseFromEvent(e);
},
{ passive: true },
);
window.addEventListener(
'scroll',
function () {
// Libera il main thread durante lo scroll; riprende subito dopo.
stop();
if (scrollPauseTimer) window.clearTimeout(scrollPauseTimer);
scrollPauseTimer = window.setTimeout(function () {
start();
}, 120);
},
{ passive: true },
);
document.addEventListener('visibilitychange', function () {
if (document.hidden) stop();
else start();
});
window.addEventListener('resize', onResize, { passive: true });
init();
start();
})();