e4e8ae4694
Hop-State: A_06FPCQR2XPMWKKCM5DJ5ZZR Hop-Proposal: R_06FPCQQAR9KPV5KP80F8430 Hop-Task: T_06FPCNFHHTPZDYDZK3WGR08 Hop-Attempt: AT_06FPCNFHHVTH3E01GZJM1YR
81 lines
2.9 KiB
JavaScript
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();
|
|
})();
|