/**
* Phantom Protocol - Главный JavaScript файл
* © NeroWorld AI, 2025
*/
(function($) {
'use strict';
// ========================================
// Инициализация при загрузке DOM
// ========================================
$(document).ready(function() {
initTheme();
initNavbar();
initAOS();
initCopyButtons();
// initHeroBackground(); // ОТКЛЮЧЕНО - используется общий background.js для всех страниц
initArchitectureAnimation();
initDemoVisualization();
initComparisonTable();
initSmoothScroll();
});
// ========================================
// Система тем (Dark/Light)
// ========================================
function initTheme() {
const themeToggle = $('#themeToggle');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const savedTheme = localStorage.getItem('phantom-theme') || (prefersDark ? 'dark' : 'light');
setTheme(savedTheme);
themeToggle.on('click', function() {
const currentTheme = $('body').attr('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
localStorage.setItem('phantom-theme', newTheme);
});
}
function setTheme(theme) {
$('body').attr('data-theme', theme);
const icon = theme === 'dark' ? 'fa-sun' : 'fa-moon';
$('#themeToggle i').removeClass('fa-moon fa-sun').addClass(icon);
}
// ========================================
// Навигация (прозрачность при скролле)
// ========================================
function initNavbar() {
$(window).on('scroll', function() {
const navbar = $('.navbar');
if ($(window).scrollTop() > 100) {
navbar.addClass('glass-nav');
} else {
navbar.removeClass('glass-nav');
}
});
}
// ========================================
// Инициализация AOS (Animate On Scroll)
// ========================================
function initAOS() {
if (typeof AOS !== 'undefined') {
AOS.init({
duration: 800,
easing: 'ease-out-cubic',
once: true,
offset: 100,
disable: 'mobile'
});
}
}
// ========================================
// Копирование команд в буфер обмена
// ========================================
function initCopyButtons() {
$('.copy-btn').on('click', function() {
const targetId = $(this).data('target');
const codeElement = $('#' + targetId);
const text = codeElement.text();
navigator.clipboard.writeText(text).then(function() {
showToast();
// Визуальная обратная связь
const btn = $(this);
const originalText = btn.html();
btn.html(' Скопировано!');
setTimeout(function() {
btn.html(originalText);
}.bind(this), 2000);
}.bind(this), function(err) {
console.error('Ошибка копирования: ', err);
});
});
}
function showToast() {
const toast = new bootstrap.Toast($('#copyToast')[0]);
toast.show();
}
// ========================================
// Hero Background - Анимированная сеть узлов
// ========================================
function initHeroBackground() {
const container = $('#heroBackground');
if (container.length === 0) return;
const canvas = $('');
container.append(canvas);
const ctx = canvas[0].getContext('2d');
// Установка размеров canvas
function resizeCanvas() {
canvas[0].width = container.width();
canvas[0].height = container.height();
}
resizeCanvas();
$(window).on('resize', resizeCanvas);
// Создание узлов сети
const nodes = [];
const nodeCount = 50;
const connectionDistance = 150;
class Node {
constructor() {
this.x = Math.random() * canvas[0].width;
this.y = Math.random() * canvas[0].height;
this.vx = (Math.random() - 0.5) * 0.5;
this.vy = (Math.random() - 0.5) * 0.5;
this.radius = Math.random() * 2 + 1;
this.opacity = Math.random() * 0.5 + 0.5;
}
update() {
this.x += this.vx;
this.y += this.vy;
// Отражение от границ
if (this.x < 0 || this.x > canvas[0].width) this.vx *= -1;
if (this.y < 0 || this.y > canvas[0].height) this.vy *= -1;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = `rgba(0, 240, 255, ${this.opacity})`;
ctx.fill();
}
}
// Создание узлов
for (let i = 0; i < nodeCount; i++) {
nodes.push(new Node());
}
// Анимационный цикл
function animate() {
ctx.clearRect(0, 0, canvas[0].width, canvas[0].height);
// Обновление и отрисовка узлов
nodes.forEach(node => {
node.update();
node.draw();
});
// Рисование соединений между близкими узлами
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
const dx = nodes[i].x - nodes[j].x;
const dy = nodes[i].y - nodes[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < connectionDistance) {
const opacity = (1 - distance / connectionDistance) * 0.3;
ctx.beginPath();
ctx.moveTo(nodes[i].x, nodes[i].y);
ctx.lineTo(nodes[j].x, nodes[j].y);
ctx.strokeStyle = `rgba(122, 62, 255, ${opacity})`;
ctx.lineWidth = 1;
ctx.stroke();
}
}
}
requestAnimationFrame(animate);
}
animate();
}
// ========================================
// Анимация архитектурных слоев
// ========================================
function initArchitectureAnimation() {
const layers = $('.arch-layer');
layers.on('mouseenter', function() {
const layer = $(this);
layer.css('transform', 'translateX(15px)');
// Пульсация номера слоя
const number = layer.find('.layer-number');
number.css('transform', 'scale(1.1)');
});
layers.on('mouseleave', function() {
const layer = $(this);
layer.css('transform', '');
const number = layer.find('.layer-number');
number.css('transform', '');
});
// Последовательная анимация при загрузке
layers.each(function(index) {
const layer = $(this);
setTimeout(function() {
layer.addClass('aos-animate');
}, index * 100);
});
}
// ========================================
// Таблица сравнения - развернуть/свернуть
// ========================================
function initComparisonTable() {
$('#expandComparison').on('click', function() {
const btn = $(this);
const isExpanded = btn.data('expanded');
if (!isExpanded) {
// Здесь можно добавить дополнительные строки
btn.html(' Свернуть таблицу');
btn.data('expanded', true);
} else {
btn.html(' Развернуть полное сравнение');
btn.data('expanded', false);
}
});
}
// ========================================
// Демо визуализация - построение анонимного пути
// ========================================
function initDemoVisualization() {
const canvas = $('#demoCanvas');
if (canvas.length === 0) return;
const ctx = canvas[0].getContext('2d');
const width = canvas[0].width;
const height = canvas[0].height;
let nodes = [];
let path = [];
let animationFrame = 0;
let isAnimating = false;
// Создание сети узлов
function createNodes() {
nodes = [];
const nodeCount = 20;
for (let i = 0; i < nodeCount; i++) {
nodes.push({
id: generatePhantomId(),
x: Math.random() * (width - 100) + 50,
y: Math.random() * (height - 100) + 50,
radius: 8,
color: '#9999b8'
});
}
drawNetwork();
}
// Генерация фантомного ID
function generatePhantomId() {
const chars = 'ABCDEF0123456789';
let id = '';
for (let i = 0; i < 8; i++) {
id += chars.charAt(Math.floor(Math.random() * chars.length));
}
return id;
}
// Отрисовка сети
function drawNetwork() {
ctx.clearRect(0, 0, width, height);
// Рисование соединений
ctx.strokeStyle = 'rgba(153, 153, 184, 0.1)';
ctx.lineWidth = 1;
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
const dx = nodes[i].x - nodes[j].x;
const dy = nodes[i].y - nodes[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 200) {
ctx.beginPath();
ctx.moveTo(nodes[i].x, nodes[i].y);
ctx.lineTo(nodes[j].x, nodes[j].y);
ctx.stroke();
}
}
}
// Рисование узлов
nodes.forEach(node => {
ctx.beginPath();
ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
ctx.fillStyle = node.color;
ctx.fill();
ctx.strokeStyle = 'rgba(0, 240, 255, 0.3)';
ctx.lineWidth = 2;
ctx.stroke();
// ID узла
ctx.fillStyle = '#e6e6ff';
ctx.font = '10px JetBrains Mono';
ctx.fillText(node.id, node.x - 25, node.y - 15);
});
}
// Построение анонимного пути
function buildPath() {
path = [];
const pathLength = Math.floor(Math.random() * 3) + 4; // 4-6 узлов
const usedIndices = new Set();
for (let i = 0; i < pathLength; i++) {
let index;
do {
index = Math.floor(Math.random() * nodes.length);
} while (usedIndices.has(index));
usedIndices.add(index);
path.push(nodes[index]);
}
}
// Анимация пути
function animatePath() {
if (!isAnimating) return;
const currentNode = Math.floor(animationFrame / 20);
if (currentNode >= path.length) {
isAnimating = false;
updateStats('complete', path.length);
return;
}
drawNetwork();
// Подсветка пройденных узлов
for (let i = 0; i <= currentNode && i < path.length; i++) {
const node = path[i];
// Пульсирующий узел
const scale = i === currentNode ? 1 + Math.sin(animationFrame * 0.2) * 0.3 : 1;
ctx.beginPath();
ctx.arc(node.x, node.y, node.radius * scale, 0, Math.PI * 2);
ctx.fillStyle = i === currentNode ? '#00f0ff' : '#7a3eff';
ctx.fill();
ctx.strokeStyle = '#00f0ff';
ctx.lineWidth = 3;
ctx.stroke();
// Соединения пути
if (i > 0) {
const prevNode = path[i - 1];
ctx.beginPath();
ctx.moveTo(prevNode.x, prevNode.y);
ctx.lineTo(node.x, node.y);
ctx.strokeStyle = '#00f0ff';
ctx.lineWidth = 2;
ctx.stroke();
// Анимация "пакета"
if (i === currentNode) {
const progress = (animationFrame % 20) / 20;
const packetX = prevNode.x + (node.x - prevNode.x) * progress;
const packetY = prevNode.y + (node.y - prevNode.y) * progress;
ctx.beginPath();
ctx.arc(packetX, packetY, 5, 0, Math.PI * 2);
ctx.fillStyle = '#00ff9d';
ctx.fill();
}
}
}
animationFrame++;
updateStats('progress', currentNode + 1);
requestAnimationFrame(animatePath);
}
// Обновление статистики
function updateStats(status, pathLength) {
if (status === 'progress') {
$('#pathLength').text(`${pathLength} / ${path.length} узлов`);
$('#entropy').text(`${(Math.log2(nodes.length * pathLength)).toFixed(2)} бит`);
$('#latency').text(`${(pathLength * 85 + Math.random() * 50).toFixed(0)} мс`);
$('#status').text('Передача...');
} else if (status === 'complete') {
$('#pathLength').text(`${pathLength} узлов`);
$('#entropy').text(`${(Math.log2(nodes.length * pathLength)).toFixed(2)} бит`);
$('#latency').text(`${(pathLength * 85).toFixed(0)} мс`);
$('#status').text('Доставлено').css('color', '#00ff9d');
} else {
$('#pathLength').text('—');
$('#entropy').text('—');
$('#latency').text('—');
$('#status').text('Готов').css('color', '');
}
}
// Кнопки управления
$('#startDemo').on('click', function() {
if (isAnimating) return;
isAnimating = true;
animationFrame = 0;
buildPath();
animatePath();
$(this).prop('disabled', true);
setTimeout(() => $(this).prop('disabled', false), 6000);
});
$('#resetDemo').on('click', function() {
isAnimating = false;
animationFrame = 0;
path = [];
createNodes();
updateStats('reset');
});
// Инициализация
createNodes();
}
// ========================================
// Плавная прокрутка к якорям
// ========================================
function initSmoothScroll() {
$('a[href^="#"]').on('click', function(e) {
const target = $(this.hash);
if (target.length) {
e.preventDefault();
$('html, body').animate({
scrollTop: target.offset().top - 80
}, 800, 'swing');
// Закрыть мобильное меню
$('.navbar-collapse').collapse('hide');
}
});
// Scroll indicator
$('.scroll-indicator').on('click', function() {
$('html, body').animate({
scrollTop: $('#usp').offset().top - 80
}, 800, 'swing');
});
}
// ========================================
// Parallax эффект для Hero секции
// ========================================
$(window).on('scroll', function() {
const scrolled = $(window).scrollTop();
const heroContent = $('.hero-content');
if (scrolled < window.innerHeight) {
heroContent.css('transform', `translateY(${scrolled * 0.5}px)`);
heroContent.css('opacity', 1 - scrolled / 600);
}
});
// ========================================
// Ленивая загрузка и оптимизация
// ========================================
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
}
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
}
// ========================================
// Прелоадер (опционально)
// ========================================
$(window).on('load', function() {
$('body').addClass('loaded');
// Trigger AOS refresh
if (typeof AOS !== 'undefined') {
AOS.refresh();
}
});
// ========================================
// Отладка: вывод версии
// ========================================
console.log('%c🔮 Phantom Protocol v2025', 'color: #00f0ff; font-size: 16px; font-weight: bold;');
console.log('%cДобро пожаловать в анонимную сеть будущего', 'color: #7a3eff; font-size: 12px;');
})(jQuery);