[Разбор] Новогодняя страница сервера — полный разбор проекта

📌 Что это за проект

Перед нами не просто праздничная открытка, а полноценная демонстрация возможностей веб-сервера, которая одновременно:

Проект раскрывает связку:
Frontend (легковесный Canvas-движок) + Backend-логика веб-сервера Nginx.

🧱 Архитектура проекта

Проект намеренно спроектирован без избыточных зависимостей и состоит всего из двух файлов:

index.html → разметка, стили и процедурный Canvas-движок
mysite.conf → боевая конфигурация сервера Nginx

Схема взаимодействия

Пользователь (Браузер)
↓
HTTPS / HTTP/2 (Nginx)
↓
sub_filter (инъекция данных на лету)
↓
index.html (HTML + CSS)
↓
Два слоя Canvas (Гирлянда + 3D-Ёлка и Салют)

🧩 Разбор файла index.html

1. Архитектура разметки и стилей

В разметке используются два независимых холста:
- #garlandsTop – адаптивная потолочная/боковая гирлянда;
- #tree – холст для отрисовки ёлки, псевдо-3D спирали, шаров и физики салютов.

🌌 Фон и адаптивность

body {
  margin: 0; min-height: 100vh; font-family: 'Segoe UI', sans-serif;
  background: radial-gradient(circle at top, #0b2a55, #000814 75%);
  color: #fff; text-align: center; overflow-x: hidden;
}

@media (max-width: 480px) {
  #garlandsTop { width: 70px !important; height: 100vh !important; }
  .hero { padding-left: 70px; padding-top: 80px; }
  .canvas-wrap { width: 340px; height: 440px; }
}

2. Умный таймер и наступление праздника

В коде реализован надёжный расчёт локального времени:

function updateTimer() {
  const now = new Date();

  // 1 января празднуем весь день
  if (now.getMonth() === 0 && now.getDate() === 1) {
    isCelebration = true;
    document.getElementById('cdBox').innerHTML = '<div class="ny-celebrate">🎄 С НОВЫМ ГОДОМ! 🎆</div>';
    return;
  }

  isCelebration = false;
  const targetDate = new Date(now.getFullYear() + 1, 0, 1, 0, 0, 0);
  const diff = targetDate - now;

  document.getElementById('d').textContent = Math.floor(diff / 864e5);
  document.getElementById('h').textContent = Math.floor(diff / 36e5) % 24;
  document.getElementById('m').textContent = Math.floor(diff / 6e4) % 60;
  document.getElementById('s').textContent = (Math.floor(diff / 1e3) % 60).toString().padStart(2, '0');
}

Почему это надёжно:
1. Персональное время: отсчёт идёт строго по локальному времени пользователя.
2. Логика первого января: когда наступает Новый год, таймер не сбрасывается ошибочно на следующие 365 дней, а переключается в праздничный режим isCelebration = true, активируя градиентный заголовок с CSS-анимацией fireworkText и автоматический запуск салютов.

3. Снегопад на CSS-анимациях

Снегопад вынесен за пределы Canvas в обычный DOM:

const snow = document.getElementById('snow');
for (let i = 0; i < 130; i++) {
  const s = document.createElement('span');
  s.style.left = Math.random() * 100 + 'vw';
  s.style.animationDuration = 6 + Math.random() * 10 + 's';
  s.style.transform = `scale(${Math.random() + 0.3})`;
  snow.appendChild(s);
}

130 снежинок двигаются при помощи GPU-ускоренного @keyframes fall { to { transform: translateY(105vh); } }. Это разгружает JavaScript и высвобождает ресурсы процессора для рендеринга сложных Canvas-эффектов.

4. Верхняя адаптивная гирлянда (#garlandsTop)

Холст динамически перестраивается под размеры экрана:

const x = isVert ? 35 + Math.sin(l.t * 15 + t * 0.002) * 15 : l.t * gCanvas.width;
const y = isVert ? l.t * gCanvas.height : 70 + Math.sin(x * 0.01 + t * 0.002) * 20;

Лампочки гирлянды колеблются по синусоиде. При горизонтальном отображении провод мягко провисает волной, а на мобильных устройствах красиво струится вдоль левой границы экрана.

🎨 Математика ёлки: псевдо-3D и физика частиц

Центральный элемент холста #tree отрисовывается в строгой последовательности слоёв:

  1. Ствол дерева
  2. Огоньки гирлянды заднего плана (z < -0.1)
  3. Процедурная хвоя (ветви)
  4. Огоньки гирлянды переднего плана (z >= -0.1)
  5. Стеклянные ёлочные шары с бликами
  6. Геометрическая пятиконечная звезда
  7. Частицы салюта (фейерверки)

🌲 Процедурная хвоя

branches.forEach((b, idx) => {
  tCtx.fillStyle = `hsla(142, 60%, ${b.c}%, 0.85)`;
  tCtx.beginPath();
  tCtx.ellipse(b.x + Math.sin(t * 0.001 + idx) * 2, b.y, 4, 11, Math.PI / 4, 0, Math.PI * 2);
  tCtx.fill();
});

Вместо простых прямоугольников ветви формируются аккуратными наклонными эллипсами с небольшим синусоидальным покачиванием на ветру. Полупрозрачность 0.85 позволяет огням заднего плана мягко мерцать сквозь глубину кроны.

🌀 Псевдо-3D спираль гирлянды

На ёлке размещено 100 дискретных источников света, распределённых по конической спирали:

const curAngle = l.baseAngle + t * speed;
const z = Math.cos(curAngle); // Координата глубины (-1 сзади, +1 спереди)
const x = 220 + Math.sin(curAngle) * radius;

Глубина сцены моделируется через координату z:
- Z-сортировка: если огонёк за ёлкой (z < -0.1), он рисуется до хвои, с пониженной яркостью (alpha ≈ 0.25) и уменьшенным радиусом.
- Если огонёк впереди (z >= -0.1), он рисуется поверх хвои, увеличивается в размере и получает сочное неоновое свечение (shadowBlur).
- Единая переменная speed = 0.0006 синхронизирует все фазы, исключая скачки и исчезновения огоньков.

🔮 Стеклянные шары с радиальным градиентом

const grad = tCtx.createRadialGradient(b.x - b.r * 0.3, b.y - b.r * 0.3, b.r * 0.1, b.x, b.y, b.r);
grad.addColorStop(0, '#fff');
grad.addColorStop(0.3, b.col);
grad.addColorStop(1, '#050505');

Иллюзия объёмного стекла достигается смещением центра радиального градиента влево-вверх и добавлением дополнительной дуги полупрозрачного белого блика.

⭐ Правильная пятиконечная звезда

Звезда строится строго по тригонометрической формуле чередования внешнего и внутреннего радиусов с шагом угла Math.PI / 5 (36 градусов):

let rot = Math.PI / 2 * 3;
tCtx.beginPath(); tCtx.moveTo(220, 50 - 15);
for (let i = 0; i < 5; i++) {
  tCtx.lineTo(220 + Math.cos(rot) * 15, 50 + Math.sin(rot) * 15); rot += Math.PI / 5;
  tCtx.lineTo(220 + Math.cos(rot) * 6, 50 + Math.sin(rot) * 6);   rot += Math.PI / 5;
}
tCtx.closePath(); tCtx.fill();

🎆 Система частиц: фейерверки над ёлкой

Частицы салюта запускаются как автоматически при наступлении праздника, так и по клику на ёлку:

function launchRocket() {
  const x = 70 + Math.random() * (tCanvas.width - 140);
  const y = 60 + Math.random() * 160;
  const col = `hsl(${Math.random() * 360}, 100%, 65%)`;
  for (let i = 0; i < 45; i++) {
    const angle = Math.random() * Math.PI * 2;
    const spd = 1.2 + Math.random() * 3.8;
    fireworks.push({
      x, y, vx: Math.cos(angle) * spd, vy: Math.sin(angle) * spd,
      alpha: 1, col, size: 2 + Math.random() * 2.2
    });
  }
}

Физика частиц учитывает простую гравитацию и затухание:
- f.vy += 0.04 (тяготение вниз);
- f.alpha -= 0.016 (угасание прозрачности);
- Отработавшие частицы автоматически удаляются из массива через splice(), предотвращая утечки оперативной памяти.

🌐 Конфигурация Nginx: серверная логика

Серверная часть обеспечивает высокую скорость доставки контента и сетевые функции прямо на уровне Nginx:

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    root /var/www/html;
    index index.html;

    # Инъекция IP пользователя прямо в тело ответа
    sub_filter_once on;
    sub_filter
      '<!-- IP_STATUS -->'
      '<div class="server-ip">Ваш IP: $remote_addr</div>';

    # Маршрут для чистой отдачи IP
    location /myip {
        default_type text/plain;
        return 200 "$remote_addr\n";
    }

    # Оптимизация отдачи статики
    gzip on;
    gzip_types text/css application/javascript;
}

# Редирект с небезопасного протокола
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Преимущества такой конфигурации:
1. Инъекция через sub_filter: Nginx подставляет реальный IP посетителя в HTML без выполнения скриптов на бэкенде.
2. Маршрут /myip: удобный эндпоинт для проверки сети через curl https://example.com/myip.
3. Безопасность: принудительный редирект HTTP на HTTPS и поддержка современного протокола HTTP/2.

📊 Сводный чек-лист проекта

Компонент Реализация Преимущество
Рендеринг Чистый Canvas 2D Context 60 кадров/сек без сторонних библиотек
3D-эффект Математический расчёт оси Z Объёмное вращение гирлянды сквозь хвою
Физика Система частиц салюта Реалистичный разлёт искр с гравитацией
Таймер Нативный Date браузера Точный учёт локального часового пояса
Адаптивность CSS Media Queries + динамический Canvas Удобство на десктопах и смартфонах
Бэкенд Nginx (sub_filter, HTTP/2, SSL) Быстрая отдача и инъекция IP без JS

🏁 Заключение

Этот проект – наглядный пример того, как грамотное применение базовой математики (синусы, полярные координаты, Z-сортировка) и штатных возможностей веб-сервера позволяет создать праздничную, технологичную и отзывчивую страницу с нулевым оверхедом по библиотекам.