/* ========================================== Phantom Protocol - Advanced Background Animation DHT Network visualization with nodes and connections Based on Hero section animation © NeroWorld AI, 2025 ========================================== */ (function() { 'use strict'; // Конфигурация const config = { nodeCount: 60, // Количество узлов DHT connectionDistance: 180, // Расстояние для связей nodeSpeedMin: 0.2, // Минимальная скорость nodeSpeedMax: 0.8, // Максимальная скорость nodeRadiusMin: 1.5, // Минимальный размер узла nodeRadiusMax: 3, // Максимальный размер узла colors: { node: 'rgba(0, 240, 255, ', // Цвет узлов (cyan) + opacity connection: 'rgba(122, 62, 255, ', // Цвет связей (purple) + opacity glow: 'rgba(0, 240, 255, 0.5)' // Цвет свечения } }; // Класс узла DHT сети class DHTNode { constructor(canvas) { this.canvas = canvas; this.reset(); } reset() { this.x = Math.random() * this.canvas.width; this.y = Math.random() * this.canvas.height; this.vx = (Math.random() - 0.5) * (config.nodeSpeedMin + Math.random() * (config.nodeSpeedMax - config.nodeSpeedMin)); this.vy = (Math.random() - 0.5) * (config.nodeSpeedMin + Math.random() * (config.nodeSpeedMax - config.nodeSpeedMin)); this.radius = config.nodeRadiusMin + Math.random() * (config.nodeRadiusMax - config.nodeRadiusMin); this.opacity = Math.random() * 0.5 + 0.5; // 0.5 - 1.0 } update() { this.x += this.vx; this.y += this.vy; // Отскок от границ if (this.x < 0 || this.x > this.canvas.width) { this.vx *= -1; this.x = Math.max(0, Math.min(this.canvas.width, this.x)); } if (this.y < 0 || this.y > this.canvas.height) { this.vy *= -1; this.y = Math.max(0, Math.min(this.canvas.height, this.y)); } } draw(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.fillStyle = config.colors.node + this.opacity + ')'; ctx.fill(); // Добавляем свечение для больших узлов if (this.radius > 2) { ctx.shadowBlur = 15; ctx.shadowColor = config.colors.glow; ctx.fill(); ctx.shadowBlur = 0; } } } // Класс анимации DHT сети class DHTNetworkAnimation { constructor(canvasId) { this.canvas = document.getElementById(canvasId); if (!this.canvas) { console.warn('Canvas element not found:', canvasId); return; } this.ctx = this.canvas.getContext('2d'); this.nodes = []; this.animationId = null; this.resizeTimeout = null; this.init(); } init() { // Установка размера canvas this.resize(); // Создание узлов DHT for (let i = 0; i < config.nodeCount; i++) { this.nodes.push(new DHTNode(this.canvas)); } // Обработчик изменения размера окна с throttling window.addEventListener('resize', () => { if (this.resizeTimeout) clearTimeout(this.resizeTimeout); this.resizeTimeout = setTimeout(() => this.resize(), 150); }); // Запуск анимации this.animate(); } resize() { if (!this.canvas) return; const newWidth = window.innerWidth; const newHeight = Math.max( document.documentElement.scrollHeight, document.body.scrollHeight, document.documentElement.clientHeight, window.innerHeight ); // Обновляем только если размер изменился if (this.canvas.width !== newWidth || this.canvas.height !== newHeight) { this.canvas.width = newWidth; this.canvas.height = newHeight; // Переинициализируем узлы при большом изменении размера if (Math.abs(this.canvas.width - newWidth) > 100) { this.nodes.forEach(node => node.reset()); } } } drawConnections() { const { connectionDistance, colors } = config; for (let i = 0; i < this.nodes.length; i++) { for (let j = i + 1; j < this.nodes.length; j++) { const dx = this.nodes[i].x - this.nodes[j].x; const dy = this.nodes[i].y - this.nodes[j].y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < connectionDistance) { // Opacity зависит от расстояния (ближе = ярче) const opacity = (1 - distance / connectionDistance) * 0.4; this.ctx.beginPath(); this.ctx.moveTo(this.nodes[i].x, this.nodes[i].y); this.ctx.lineTo(this.nodes[j].x, this.nodes[j].y); this.ctx.strokeStyle = colors.connection + opacity + ')'; this.ctx.lineWidth = 1; this.ctx.stroke(); } } } } animate() { if (!this.canvas || !this.ctx) return; // Очистка canvas this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Рисуем связи первыми (за узлами) this.drawConnections(); // Обновление и отрисовка узлов this.nodes.forEach(node => { node.update(); node.draw(this.ctx); }); // Следующий кадр this.animationId = requestAnimationFrame(() => this.animate()); } destroy() { if (this.animationId) { cancelAnimationFrame(this.animationId); this.animationId = null; } if (this.resizeTimeout) { clearTimeout(this.resizeTimeout); } this.nodes = []; } } // Автоматическая инициализация при загрузке function initBackground() { // Проверяем что DOM загружен if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', startAnimation); } else { startAnimation(); } function startAnimation() { // Ждем полной загрузки страницы if (document.readyState === 'complete') { createAnimation(); } else { window.addEventListener('load', createAnimation); } } function createAnimation() { setTimeout(() => { // Создаем анимацию window.phantomBackground = new DHTNetworkAnimation('phantomBackground'); // Отслеживание изменений DOM (с throttling) if (window.phantomBackground) { let mutationTimeout; const observer = new MutationObserver(() => { if (mutationTimeout) clearTimeout(mutationTimeout); mutationTimeout = setTimeout(() => { if (window.phantomBackground && typeof window.phantomBackground.resize === 'function') { window.phantomBackground.resize(); } }, 1000); // Задержка 1 секунда для стабильности }); // Наблюдаем за изменениями структуры страницы observer.observe(document.body, { childList: true, subtree: false }); } console.log('✨ DHT Network Background initialized'); }, 100); } } // Запуск initBackground(); })();