Files
Phantom/website/js/background.js

238 lines
9.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ==========================================
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();
})();