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
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
(() => {
|
||||
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();
|
||||
})();
|
||||
Reference in New Issue
Block a user