Новогодний снегопад с поздравлением для любого сайта, скрипт

Украсить ваш сайт снежинками и поздравлениями с новым годом:

Код вставляем перерд закрывающим

Пздравления и снег:


<script>
/*
 * ============================================================
 *  ❄️ НОВОГОДНИЙ СНЕГОПАД С ПОЗДРАВЛЕНИЕМ (оптимизированный) ❄️
 * ============================================================
 *  Автор: Ведерников Сергей
 *  ИИ:    Дэп — https://1promtai.ru/
 * ============================================================
 *  Оптимизация: снежинки рисуются 1 раз в offscreen-канвас,
 *  затем штампуются через drawImage(). Без shadowBlur.
 * ============================================================
 */
(function() {
  'use strict';

  const CONFIG = {
    back: {
      count: 35,                  // было 50 — меньше нагрузка
      minSize: 3,
      maxSize: 6,
      minSpeed: 0.2,
      maxSpeed: 0.7,
      swayAmplitude: 1.0,
      rotationSpeed: 0.004,
      opacityMin: 0.15,
      opacityMax: 0.35,
      colors: ['30,100,220', '50,120,230', '70,140,240']
    },
    front: {
      count: 18,                  // было 25 — меньше нагрузка
      minSize: 5,
      maxSize: 10,
      minSpeed: 0.6,
      maxSpeed: 1.6,
      swayAmplitude: 1.5,
      rotationSpeed: 0.012,
      opacityMin: 0.6,
      opacityMax: 1.0,
      colors: ['20,80,200', '30,100,220', '40,110,235', '60,130,245']
    },
    greetingInterval: 60000,
    greetingDuration: 6000,
    greetings: [
      '🎄 С Новым годом! 🎄',
      '❄️ Счастья и удачи в новом году! ❄️',
      '🎁 Пусть всё загаданное сбудется! 🎁',
      '✨ С Новым годом, дорогие друзья! ✨',
      '⛄ Мира, тепла и волшебства! ⛄',
      '🥂 За новый счастливый год! 🥂'
    ]
  };

  // ===== CANVAS =====
  const canvas = document.createElement('canvas');
  canvas.style.cssText =
    'position:fixed;top:0;left:0;width:100%;height:100%;' +
    'pointer-events:none;z-index:9998;';
  canvas.setAttribute('aria-hidden', 'true');
  document.body.appendChild(canvas);

  const ctx = canvas.getContext('2d', { alpha: true });
  let W = window.innerWidth;
  let H = window.innerHeight;

  function resize() {
    W = window.innerWidth;
    H = window.innerHeight;
    const dpr = Math.min(window.devicePixelRatio || 1, 2); // ограничили 2 — на 3x дорого
    canvas.width = W * dpr;
    canvas.height = H * dpr;
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  }
  resize();

  // resize — с дебаунсом, чтобы не пересчитывать при каждом пикселе
  let resizeTimer;
  window.addEventListener('resize', function() {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(resize, 200);
  });

  // ===== ПРЕДВАРИТЕЛЬНАЯ ОТРИСОВКА СНЕЖИНОК (кэш) =====
  // Для каждой комбинации (размер + цвет) создаём offscreen-canvas
  // один раз при старте, потом только drawImage.
  const SPRITE_PADDING = 4;    // запас под сглаживание
  const spriteCache = {};      // ключ: color|size → { canvas, half }

  function makeSnowflakeSprite(size, color) {
    const key = color + '|' + size.toFixed(1);
    if (spriteCache[key]) return spriteCache[key];

    const half = Math.ceil(size + SPRITE_PADDING);
    const full = half * 2;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);

    const spr = document.createElement('canvas');
    spr.width = full * dpr;
    spr.height = full * dpr;

    const sctx = spr.getContext('2d');
    sctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    sctx.translate(half, half);

    const lineWidth = Math.max(0.6, size * 0.09);
    const stroke = 'rgba(' + color + ',1)';
    sctx.strokeStyle = stroke;
    sctx.fillStyle = stroke;
    sctx.lineWidth = lineWidth;
    sctx.lineCap = 'round';

    // 6 лучей с веточками
    for (let i = 0; i < 6; i++) {
      const angle = (Math.PI / 3) * i;
      const dx = Math.cos(angle);
      const dy = Math.sin(angle);

      sctx.beginPath();
      sctx.moveTo(0, 0);
      sctx.lineTo(dx * size, dy * size);
      sctx.stroke();

      const branchPositions = [0.4, 0.7];
      const branchLength = size * 0.35;
      const branchAngle = Math.PI / 5;

      for (let b = 0; b < branchPositions.length; b++) {
        const pos = branchPositions[b];
        const bx = dx * size * pos;
        const by = dy * size * pos;
        const bLen = branchLength * (b === 0 ? 1 : 0.7);

        sctx.beginPath();
        sctx.moveTo(bx, by);
        sctx.lineTo(
          bx + Math.cos(angle - branchAngle) * bLen,
          by + Math.sin(angle - branchAngle) * bLen
        );
        sctx.stroke();

        sctx.beginPath();
        sctx.moveTo(bx, by);
        sctx.lineTo(
          bx + Math.cos(angle + branchAngle) * bLen,
          by + Math.sin(angle + branchAngle) * bLen
        );
        sctx.stroke();
      }
    }

    // центральная точка
    sctx.beginPath();
    sctx.arc(0, 0, lineWidth * 0.9, 0, Math.PI * 2);
    sctx.fill();

    const sprite = { canvas: spr, half: half };
    spriteCache[key] = sprite;
    return sprite;
  }

  // Округляем размеры до 0.5 — кэш не разрастается
  function snapSize(s) {
    return Math.round(s * 2) / 2;
  }

  // ===== СНЕЖИНКИ =====
  function createFlake(layer, randomY) {
    const rawSize = layer.minSize + Math.random() * (layer.maxSize - layer.minSize);
    const size = snapSize(rawSize);
    const color = layer.colors[(Math.random() * layer.colors.length) | 0];
    const sprite = makeSnowflakeSprite(size, color);

    return {
      x: Math.random() * W,
      y: randomY ? Math.random() * H : -20,
      size: size,
      sprite: sprite,
      speed: layer.minSpeed + Math.random() * (layer.maxSpeed - layer.minSpeed),
      sway: Math.random() * Math.PI * 2,
      swaySpeed: 0.01 + Math.random() * 0.02,
      rotation: Math.random() * Math.PI * 2,
      rotationSpeed: (Math.random() - 0.5) * layer.rotationSpeed * 2,
      opacity: layer.opacityMin + Math.random() * (layer.opacityMax - layer.opacityMin),
      layer: layer
    };
  }

  function createLayer(cfg) {
    const arr = new Array(cfg.count);
    for (let i = 0; i < cfg.count; i++) arr[i] = createFlake(cfg, true);
    return arr;
  }

  const backFlakes = createLayer(CONFIG.back);
  const frontFlakes = createLayer(CONFIG.front);

  // ===== АНИМАЦИЯ =====
  function updateFlakes(flakes, dt) {
    for (let i = 0; i < flakes.length; i++) {
      const f = flakes[i];
      f.y += f.speed * dt;
      f.sway += f.swaySpeed * dt;
      f.rotation += f.rotationSpeed * dt;

      const x = f.x + Math.sin(f.sway) * f.layer.swayAmplitude;
      const spr = f.sprite;

      if (f.opacity < 0.99) ctx.globalAlpha = f.opacity;
      else ctx.globalAlpha = 1;

      // drawImage с вращением
      ctx.save();
      ctx.translate(x, f.y);
      if (f.rotation !== 0) ctx.rotate(f.rotation);
      ctx.drawImage(spr.canvas, -spr.half, -spr.half, spr.half * 2, spr.half * 2);
      ctx.restore();

      if (f.y - f.size > H) {
        flakes[i] = createFlake(f.layer, false);
      }
    }
    ctx.globalAlpha = 1;
  }

  let lastTime = performance.now();
  function animate(now) {
    const dt = Math.min((now - lastTime) / 16.67, 3);
    lastTime = now;

    ctx.clearRect(0, 0, W, H);
    updateFlakes(backFlakes, dt);
    updateFlakes(frontFlakes, dt);

    requestAnimationFrame(animate);
  }
  requestAnimationFrame(animate);

  // ===== ПЛАШКА ПОЗДРАВЛЕНИЯ =====
  const greetingEl = document.createElement('div');
  greetingEl.style.cssText = [
    'position:fixed',
    'top:50%',
    'left:50%',
    'transform:translate(-50%,-50%) scale(0.6)',
    'background:linear-gradient(135deg,#0a1f4a,#143a80,#1e5bc8)',
    'color:#fff',
    'padding:28px 44px',
    'border-radius:20px',
    'font-family:-apple-system,"Segoe UI",Roboto,Arial,sans-serif',
    'font-size:clamp(20px,4vw,42px)',
    'font-weight:700',
    'text-align:center',
    'box-shadow:0 0 40px rgba(50,130,255,0.7),0 0 80px rgba(30,100,220,0.4)',
    'border:1px solid rgba(120,180,255,0.5)',
    'text-shadow:0 0 12px rgba(120,180,255,0.9)',
    'pointer-events:none',
    'opacity:0',
    'transition:opacity 0.6s ease,transform 0.6s cubic-bezier(0.18,0.89,0.32,1.28)',
    'z-index:9999',
    'max-width:90vw',
    'line-height:1.3'
  ].join(';');
  greetingEl.setAttribute('aria-hidden', 'true');
  document.body.appendChild(greetingEl);

  let lastGreetingIndex = -1;
  function showGreeting() {
    let idx;
    do {
      idx = (Math.random() * CONFIG.greetings.length) | 0;
    } while (idx === lastGreetingIndex && CONFIG.greetings.length > 1);
    lastGreetingIndex = idx;

    greetingEl.textContent = CONFIG.greetings[idx];
    greetingEl.style.opacity = '1';
    greetingEl.style.transform = 'translate(-50%,-50%) scale(1)';

    setTimeout(function() {
      greetingEl.style.opacity = '0';
      greetingEl.style.transform = 'translate(-50%,-50%) scale(0.6)';
    }, CONFIG.greetingDuration);
  }

  setTimeout(showGreeting, 2000);
  setInterval(showGreeting, CONFIG.greetingInterval);
})();
</script>


Оставить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

🧮 Проверка безопасности
10 - 7 = ?