Files
Synapsis/marketing/script.js
T
cyph3rasi e4e8ae4694 Add a complete static Synapsis marketing site with responsive swarm visuals, product story, operator guide, and social preview
Hop-State: A_06FPCQR2XPMWKKCM5DJ5ZZR
Hop-Proposal: R_06FPCQQAR9KPV5KP80F8430
Hop-Task: T_06FPCNFHHTPZDYDZK3WGR08
Hop-Attempt: AT_06FPCNFHHVTH3E01GZJM1YR
2026-07-15 08:22:10 -07:00

81 lines
2.9 KiB
JavaScript

(() => {
const header = document.querySelector('.site-header');
const year = document.querySelector('#year');
if (year) year.textContent = String(new Date().getFullYear());
const updateHeader = () => header?.classList.toggle('scrolled', window.scrollY > 24);
updateHeader();
window.addEventListener('scroll', updateHeader, { passive: true });
const canvas = document.querySelector('.signal-field');
if (!(canvas instanceof HTMLCanvasElement) || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const context = canvas.getContext('2d');
if (!context) return;
let width = 0;
let height = 0;
let ratio = 1;
let points = [];
const pointer = { x: -1000, y: -1000 };
const reset = () => {
ratio = Math.min(window.devicePixelRatio || 1, 2);
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width * ratio;
canvas.height = height * ratio;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
const count = Math.min(58, Math.max(26, Math.round(width / 25)));
points = Array.from({ length: count }, () => ({
x: Math.random() * width,
y: Math.random() * height,
vx: (Math.random() - 0.5) * 0.16,
vy: (Math.random() - 0.5) * 0.16,
r: Math.random() * 1.2 + 0.5,
}));
};
const render = () => {
context.clearRect(0, 0, width, height);
points.forEach((point, index) => {
point.x += point.vx;
point.y += point.vy;
if (point.x < -10) point.x = width + 10;
if (point.x > width + 10) point.x = -10;
if (point.y < -10) point.y = height + 10;
if (point.y > height + 10) point.y = -10;
const pointerDistance = Math.hypot(point.x - pointer.x, point.y - pointer.y);
const glow = pointerDistance < 180;
context.fillStyle = glow ? 'rgba(200,255,69,.82)' : 'rgba(142,162,160,.35)';
context.beginPath();
context.arc(point.x, point.y, glow ? point.r + 0.8 : point.r, 0, Math.PI * 2);
context.fill();
for (let j = index + 1; j < points.length; j += 1) {
const other = points[j];
const distance = Math.hypot(point.x - other.x, point.y - other.y);
if (distance < 112) {
context.strokeStyle = `rgba(130,150,148,${(1 - distance / 112) * 0.085})`;
context.lineWidth = 1;
context.beginPath();
context.moveTo(point.x, point.y);
context.lineTo(other.x, other.y);
context.stroke();
}
}
});
requestAnimationFrame(render);
};
window.addEventListener('resize', reset, { passive: true });
window.addEventListener('pointermove', (event) => {
pointer.x = event.clientX;
pointer.y = event.clientY;
}, { passive: true });
window.addEventListener('pointerleave', () => { pointer.x = -1000; pointer.y = -1000; });
reset();
render();
})();