Initial commit: sito NexStudio (Astro) con Cloudflare, CMS cookie e contenuti legali
- Home: hero, prodotti, servizi, stack, FAQ, CTA con form contatti inline (glossy) e particelle - Header nero, menu con scroll-spy e alone su voce attiva, CTA Chattiamo (pre-chat) - Pagine: privacy, cookie, GDPR, terms, about, codice etico, modello organizzativo, dove siamo, news - Cookie consent first-party, chat gate, stampe-friendly su SubpageLayout - Footer: Sezioni con Contattaci, Azienda con Dove siamo senza link per ora - .gitignore: aggiunto .wrangler/ per stato locale Cloudflare Made-with: Cursor
This commit is contained in:
173
public/particle-bg.js
Normal file
173
public/particle-bg.js
Normal file
@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Rete di particelle + linee (canvas #particleCanvas).
|
||||
* Deve stare DENTRO il contenitore della pagina (es. .nx-grid-bg), non dietro body,
|
||||
* altrimenti lo sfondo opaco della pagina lo copre.
|
||||
*/
|
||||
(function () {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
var canvas = document.getElementById('particleCanvas');
|
||||
if (!canvas) return;
|
||||
var ctx = canvas.getContext('2d');
|
||||
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;
|
||||
|
||||
function canvasSize() {
|
||||
var p = canvas.parentElement;
|
||||
if (!p) return { w: 0, h: 0 };
|
||||
var w = Math.max(1, Math.floor(p.clientWidth));
|
||||
var h = Math.max(1, Math.floor(p.clientHeight));
|
||||
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.5;
|
||||
this.speedY = (Math.random() - 0.5) * 1.5;
|
||||
}
|
||||
|
||||
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 * 5;
|
||||
this.y -= fy * force * 5;
|
||||
}
|
||||
};
|
||||
|
||||
function init() {
|
||||
var sz = canvasSize();
|
||||
var w = sz.w;
|
||||
var h = sz.h;
|
||||
if (w < 8 || h < 8) return;
|
||||
|
||||
var dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||
canvas.width = Math.floor(w * dpr);
|
||||
canvas.height = Math.floor(h * dpr);
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
|
||||
particles = [];
|
||||
var n = Math.floor((w * h) / 9000);
|
||||
if (n > 130) n = 130;
|
||||
if (n < 45) n = 45;
|
||||
for (var i = 0; i < n; i++) {
|
||||
particles.push(new Particle(w, h));
|
||||
}
|
||||
}
|
||||
|
||||
function connect(w, h) {
|
||||
var a;
|
||||
var b;
|
||||
var dx;
|
||||
var dy;
|
||||
var distance;
|
||||
var opacity;
|
||||
for (a = 0; a < particles.length; a++) {
|
||||
for (b = a + 1; b < particles.length; b++) {
|
||||
dx = particles[a].x - particles[b].x;
|
||||
dy = particles[a].y - particles[b].y;
|
||||
distance = Math.sqrt(dx * dx + dy * dy);
|
||||
if (distance < 150) {
|
||||
opacity = (1 - distance / 150) * 0.22;
|
||||
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() {
|
||||
var sz = canvasSize();
|
||||
var w = sz.w;
|
||||
var h = sz.h;
|
||||
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, h);
|
||||
raf = requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
function syncCanvasCssSize() {
|
||||
var sz = canvasSize();
|
||||
canvas.style.width = sz.w + 'px';
|
||||
canvas.style.height = sz.h + 'px';
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
cancelAnimationFrame(raf);
|
||||
syncCanvasCssSize();
|
||||
init();
|
||||
if (!reduced && particles.length) {
|
||||
raf = requestAnimationFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
function setMouseFromEvent(e) {
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
mouse.x = e.clientX - rect.left;
|
||||
mouse.y = e.clientY - rect.top;
|
||||
}
|
||||
|
||||
window.addEventListener(
|
||||
'mousemove',
|
||||
function (e) {
|
||||
setMouseFromEvent(e);
|
||||
},
|
||||
{ passive: true },
|
||||
);
|
||||
|
||||
var parent = canvas.parentElement;
|
||||
if (parent) {
|
||||
var ro = new ResizeObserver(onResize);
|
||||
ro.observe(parent);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', onResize, { passive: true });
|
||||
|
||||
syncCanvasCssSize();
|
||||
init();
|
||||
|
||||
if (reduced) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (particles.length) {
|
||||
raf = requestAnimationFrame(frame);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user