90 lines
2.7 KiB
JavaScript
90 lines
2.7 KiB
JavaScript
const yearElement = document.querySelector("#current-year");
|
|
const canvas = document.querySelector("#network-canvas");
|
|
|
|
if (yearElement) {
|
|
yearElement.textContent = new Date().getFullYear().toString();
|
|
}
|
|
|
|
if (canvas) {
|
|
const context = canvas.getContext("2d");
|
|
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
let animationFrame = null;
|
|
const nodes = Array.from({ length: 42 }, (_, index) => ({
|
|
x: (index * 97) % 100,
|
|
y: (index * 53) % 100,
|
|
speed: 0.08 + (index % 5) * 0.015,
|
|
phase: index * 0.7,
|
|
}));
|
|
|
|
function resizeCanvas() {
|
|
const ratio = Math.min(window.devicePixelRatio || 1, 2);
|
|
const { width, height } = canvas.getBoundingClientRect();
|
|
|
|
canvas.width = Math.max(1, Math.floor(width * ratio));
|
|
canvas.height = Math.max(1, Math.floor(height * ratio));
|
|
context.setTransform(ratio, 0, 0, ratio, 0, 0);
|
|
}
|
|
|
|
function drawNetwork(time = 0) {
|
|
const { width, height } = canvas.getBoundingClientRect();
|
|
const points = nodes.map((node) => {
|
|
const drift = prefersReducedMotion.matches ? 0 : time * 0.001 * node.speed;
|
|
|
|
return {
|
|
x: ((node.x + Math.sin(node.phase + drift) * 5 + 100) % 100) * width / 100,
|
|
y: ((node.y + Math.cos(node.phase + drift) * 5 + 100) % 100) * height / 100,
|
|
};
|
|
});
|
|
|
|
context.clearRect(0, 0, width, height);
|
|
context.fillStyle = "#070a0f";
|
|
context.fillRect(0, 0, width, height);
|
|
|
|
context.lineWidth = 1;
|
|
for (let index = 0; index < points.length; index += 1) {
|
|
for (let nextIndex = index + 1; nextIndex < points.length; nextIndex += 1) {
|
|
const first = points[index];
|
|
const second = points[nextIndex];
|
|
const distance = Math.hypot(first.x - second.x, first.y - second.y);
|
|
|
|
if (distance < 170) {
|
|
const alpha = (1 - distance / 170) * 0.22;
|
|
context.strokeStyle = `rgba(103, 232, 249, ${alpha})`;
|
|
context.beginPath();
|
|
context.moveTo(first.x, first.y);
|
|
context.lineTo(second.x, second.y);
|
|
context.stroke();
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const point of points) {
|
|
context.fillStyle = "rgba(124, 247, 182, 0.88)";
|
|
context.beginPath();
|
|
context.arc(point.x, point.y, 2.2, 0, Math.PI * 2);
|
|
context.fill();
|
|
}
|
|
|
|
if (!prefersReducedMotion.matches) {
|
|
animationFrame = window.requestAnimationFrame(drawNetwork);
|
|
}
|
|
}
|
|
|
|
resizeCanvas();
|
|
drawNetwork();
|
|
window.addEventListener("resize", () => {
|
|
resizeCanvas();
|
|
|
|
if (prefersReducedMotion.matches) {
|
|
drawNetwork();
|
|
}
|
|
});
|
|
prefersReducedMotion.addEventListener("change", () => {
|
|
if (animationFrame) {
|
|
window.cancelAnimationFrame(animationFrame);
|
|
animationFrame = null;
|
|
}
|
|
|
|
drawNetwork();
|
|
});
|
|
}
|