Обновить

Комментарии 16

Мгновенная белая вспышка (createFlashEffect) озаряет всё вокруг

В конверте была противопехотная мина

Спасибо! Аналогия с яркой вспышкой (createFlashEffect) была удачной. Взял её за основу, немного переработав формулировку, чтобы звучало немного посмешнее.

буду искренне рад вашей обратной связи

Раз такое дело, то вот обратная связь: текст и код написаны ллм, от чего уже давно воротит. На техническом сайте "статья" уровня школьного сочинения о воспоминаниях друга, который порадовал девочку... Что, простите? Три файла названы архитектурой.

Мы живём в эпоху, когда сообщение легко заменяет разговор за чашкой чая, но такие проекты напоминают, что

...не надо использовать технологии по поводу и без ради пиара корпоративного блога. И handmade здесь нет — весь визуальный стиль от ллм. Это не красиво, а пошло.

Спасибо за комментарий!
Хотел начать с вашей последней мысли, что handmade тут нет — тут не согласен. Всё, что вы делаешь — даже сейчас популярное оживление фотографий через ИИ — это handmade (вы потратил время, придумал идею, и вещь, которую вы сделали, тоже уникальна).

Насчёт нейронки: да, в этой статье и как в самой первой, где мне тоже писали про ИИ, он есть, и я не скрываю — часто ей пользуюсь, но не всё написано с его помощью от начала до конца. Думаю, сейчас любой человек так или иначе использует LLM-инструменты в работе.

А так был бы рад получить обратную связь не в формате "всё плохо, надо по-другому, вот раньше было лучше", а скорее: "во введении хотелось бы увидеть больше про личную историю проекта" или "не пишите весь код — зайти на GitHub не проблема, лучше покажите ключевые моменты" или "вот пример хорошей статьи, возьмите отсюда логику и структуру".

Вот такие комментарии хотелось бы получать, но всё равно спасибо — даже критика помогает двигаться вперёд.

это handmade (вы потратил время, придумал идею, и вещь, которую вы сделали, тоже уникальна)

Чушь. Не надо врать на преимущественно техническом ресурсе. Вы не умнее аудитории. Единственное потраченное время — это время людей, которые прочитали "это". 7,6к просмотров, допустим, в среднем по 5 минут — это 633 часа в никуда. Составление промпта требует усилий? С таким качеством материала это не так.

Единственным верным вариантом в данном случае было молчание. Но вы выбрали "качать права". Слабоумие и отвага in a nutshell.

А так был бы рад получить обратную связь не в формате "всё плохо, надо по-другому, вот раньше было лучше", а скорее:

Вы просили обратную связь? Вы ее получили. Уверен, что ллм вас достаточно похвалила за гениальную и свежую идею.

Вы или намеренно делаете вид, что не поняли, что вам написали, или реально думаете "а чо такова?". В обоих случаях плохо. Ваша статья — да это даже не статья, а нейромусор. Люди устали от этого.

Всё, что вы тут выложили, умещается в одном промпте: "создай интерактивную поздравительную открытку на js" — в этом ноль ценности.

Чем ваша статья отличается от других нейроночных? Ничем.

Какая есть польза от вашей статьи читателям? Никакой.

Чему учит ваша статья? Ничему.

В ней буквально ничего нет. В том числе и усилий человека. В оба моих комментария вложено в разы больше смысла. За свой низкокачественный материал вы получили заслуженный комментарий. Хотите делать лучше? Ищите сами, пока усилий не видно от слова "совсем". Но вам еще хватает наглости перекладывать ответственность на читателя. Это повод забросить и вас и блог компании в чс. Как вы к нам, так и мы к вам.

Согласен, стоило промолчать. Каждый всё равно остался бы при своём мнении.

Да ладно вам. @MisterKot прав. Статья - отстой сгенерированный LLM и чуточку причёсаный человеком. Ценности - ноль.

Я бы мимо прошёл и промолчал, но так совпало, что мне она (ваша статья) выпала в сайдбаре, когда я вот эту статью читал - https://habr.com/ru/companies/ruvds/articles/940326/

Тоже от ruvds, но уровнем (и вложенным усилием) выше на порядки. Эх :(.

Но раз вы считаете что статья ценная, то пусть так и будет. Жду статью про архитектуру скрипта как добавить снежинки на страницу с помощью javascript.

Эх, а ведь раньше были прям классные статьи у ruvds :(

Причем ллм какая-то сомнительная была, дипсик? Визуал позорный, даже шрифты не сменили, сейчас всякие клоды с одного маленького промпта делают нестыдные лендинги

Спасибо, весьма забавный хороший проект.

В 2025 году тоже экспериментировал, но поместил весь код в один файл для удобства передачи (а также быстрой загрузки на какой-нибудь хостинг, хотя конечно лучше разбивать на части).

В общем, из интересного, по крайней мере в Chromium-based браузерах можно через js инициировать закрытие вкладки (после завершения поздравления) + удалось настроить автоперевод телефона в полноэкранный режим, что позволяет лучше погрузиться в атмосферу поздравления.

Если кому интересно, могу прислать код ниже (disclaimer: свои эксперименты завершил объединением своих кода и кода с SO в нормально рабочий отшлифованный код ИИ, но главное, работает).

Обошёлся без торта, как помню, но сыплющиеся конфетти и ещё что-то поместил.

Спасибо, рад, что наши идеи совпали, если поделитесь кодом — будет супер, с большим интересом изучу.

Код
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>С Днём Рождения, Мамочка!</title>
<style>
  :root{
    --bg:#f4e2c9;       /* тёплый бежевый — как на открытке */
    --ink:#7a2b1f;      /* тёплый коричневый для текста */
    --leaf:#3c8a64;     /* зелень */
    --accent:#ef7f3c;   /* оранжевый бант/цветы */
    --soft:#f5a08f;     /* мягкий розовый */
  }
  html,body{
    margin:0; padding:0; height:100%; background:var(--bg);
    font-family: "Segoe UI", system-ui, -apple-system, Arial, sans-serif;
    color:var(--ink);
    overflow:hidden; /* на время анимаций */
  }
  img {
  	object-fit: contain;
  }
  .stage{
    position:relative; width:100%; height:100%;
    display:grid; place-items:center;
  }

  /* SVG открытка: адаптивная, без полос */
  .card{
    width:min(92vw, 900px);
    aspect-ratio: 3 / 4; /* как у постера */
    display:block;
    background:transparent;
  }

  /* Кнопка-сюрприз: появляется через 1с, сияет и блестит */
  .cta-wrap{
    position:absolute; inset:auto 0 8vh 0; display:flex; justify-content:center;
    opacity:0; transform:translateY(12px); pointer-events:none;
    transition:opacity .6s ease, transform .6s ease;
  }
  .cta-wrap.show{ opacity:1; transform:none; pointer-events:auto; }

  .cta{
    font-size:clamp(16px, 3.5vw, 28px);
    padding:.9em 1.4em;
    border:none; border-radius:999px; cursor:pointer;
    background:linear-gradient(180deg, #fff5e8, #ffdcb6);
    color:#8b3a24; font-weight:700; letter-spacing:.4px;
    box-shadow:0 0 0 0 rgba(255,168,82,.7), 0 10px 24px rgba(139,58,36,.25);
    position:relative; outline:none;
    transition:transform .15s ease;
    animation: glow 2.2s ease-in-out infinite;
  }
  .cta:active{ transform:scale(.98); }
  @keyframes glow{
    0%,100%{ box-shadow:0 0 0 0 rgba(255,168,82,.7), 0 10px 24px rgba(139,58,36,.25); }
    50%{    box-shadow:0 0 32px 8px rgba(255,168,82,.65), 0 10px 28px rgba(139,58,36,.35); }
  }
  /* Блески */
  .cta::before, .cta::after{
    content:""; position:absolute; inset:-6px; border-radius:inherit; pointer-events:none;
    background:
      radial-gradient(circle at 15% 30%, rgba(255,255,255,.8) 0 6px, transparent 7px),
      radial-gradient(circle at 80% 60%, rgba(255,255,255,.7) 0 7px, transparent 8px),
      radial-gradient(circle at 55% 15%, rgba(255,255,255,.9) 0 4px, transparent 5px);
    mix-blend-mode:screen; filter:blur(.3px);
    animation:sparkle 3.5s linear infinite;
  }
  .cta::after{ animation-duration:2.4s; animation-direction:reverse; opacity:.7; }
  @keyframes sparkle{
    0%{ transform:translateY(0) rotate(0deg); opacity:.9; }
    50%{ transform:translateY(-6px) rotate(10deg); opacity:.7; }
    100%{ transform:translateY(0) rotate(0deg); opacity:.9; }
  }

  /* Кнопка «Выход» появляется через 6с */
  .exit{
    position:fixed; left:50%; bottom:14px; transform:translateX(-50%);
    padding:.7em 1.2em; border-radius:14px; border:1px solid rgba(0,0,0,.08);
    background:#ffffffcc; backdrop-filter:blur(6px);
    font-size:clamp(14px, 2.5vw, 18px); color:#5a2a1f; font-weight:600;
    box-shadow:0 8px 20px rgba(0,0,0,.12);
    display:none; cursor:pointer;
  }
  .exit.show{ display:inline-block; }

  /* Конфетти-канвас */
  #confetti{
    position:fixed; inset:0; pointer-events:none; z-index:5;
  }

  /* Подпись снизу открытки */
  .sig{
    position:absolute; bottom:4vh; left:0; right:0; text-align:center;
    font-size:clamp(14px, 2.2vw, 18px); opacity:.75;
  }
</style>
</head>
<body>
  <canvas id="confetti"></canvas>

  <div class="stage" id="stage">
    <!-- Векторная открытка с внешним PNG-букетом, вставленным в SVG.
         Изображение будет подгружено по указанной ссылке. -->
    <svg class="card" viewBox="0 0 900 1200" role="img" aria-label="С Днём Рождения, Мамочка!">
      <defs>
        <filter id="softShadow" x="-20%" y="-20%" width="140%" height="140%">
          <feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#8b3a24" flood-opacity=".12"/>
        </filter>
      </defs>

      <!-- Заголовок -->
      <g filter="url(#softShadow)">
        <text x="450" y="150" text-anchor="middle"
              style="font:900 74px/1 'Segoe UI', Arial, sans-serif; letter-spacing:3px; fill:#7a2b1f;">
          С ДНЁМ РОЖДЕНИЯ,
        </text>
        <text x="450" y="240" text-anchor="middle"
              style="font:900 74px/1 'Segoe UI', Arial, sans-serif; letter-spacing:3px; fill:#7a2b1f;">
          МАМОЧКА!
        </text>
      </g>

      <!-- Внешнее PNG-изображение букета (вставлено по ссылке пользователя) -->
      <!-- Позиция и размеры подобраны, чтобы изображение вписалось в область букета. -->
      <image href="https://img.freepik.com/free-vector/bouquet-pink-roses_1308-6783.jpg?t=st=1770809051~exp=1770812651~hmac=712b3e3388943911122b3119d4a7490977c488b714f8bb361fce0e8200c6e191" 
             x="90" y="280" width="720" height="640" preserveAspectRatio="xMidYMid meet" />
<!--была картинка https://archive.org/download/file_000000000a3861f9b397f591381ec940/file_000000000a3861f9b397f591381ec940.png, но жилеты её удалили -->
      <!-- Нижняя подпись -->
      <text x="450" y="1130" text-anchor="middle"
            style="font:italic 700 58px/1 'Segoe UI', Arial, sans-serif; fill:#7a2b1f;">
        Люблю тебя!
      </text>
    </svg>

    <div class="cta-wrap" id="ctaWrap">
      <button class="cta" id="ctaBtn" aria-label="Праздничная кнопка">Сюрприз!</button>
    </div>

    <div class="sig">Поздравляю! 💖</div>
  </div>

  <button class="exit" id="exitBtn" title="Закрыть вкладку">Выход</button>

<script>
/* === Полноэкранный режим по возможности === */
(async function goFullscreen(){
  try{
    // Попытка автоматически перейти в полноэкранный режим при загрузке.
    if (document.fullscreenEnabled) {
      await document.documentElement.requestFullscreen();
    }
  }catch(e){ /* браузер мог заблокировать автопереход — дальше есть повтор при клике */ }

  // Повторный запрос при первом клике (работает в браузерах, где требуется пользовательское действие)
  const once = (fn)=>{ let done=false; return (...a)=>{ if(!done){ done=true; fn(...a); } } };
  const retryFS = once(async ()=>{
    try{
      if (!document.fullscreenElement && document.fullscreenEnabled) {
        await document.documentElement.requestFullscreen();
      }
    }catch(e){}
  });
  addEventListener('click', retryFS, { once:true });
})();

/* === Появление сияющей кнопки через 1 секунду === */
setTimeout(()=>document.getElementById('ctaWrap').classList.add('show'), 1000);

/* === Конфетти === */
(function(){
  const cvs = document.getElementById('confetti');
  const ctx = cvs.getContext('2d');
  let W, H, running = false, pieces = [];

  function resize(){
    W = cvs.width = innerWidth;
    H = cvs.height = innerHeight;
  }
  addEventListener('resize', resize); resize();

  function rand(min, max){ return Math.random()*(max-min)+min; }
  const COLORS = ['#ff6b6b','#ffd93d','#6bcB77','#4d96ff','#ff9f1c','#9b5de5','#00bbf9','#f15bb5'];

  function makePiece(){
    const shape = Math.random()<0.5 ? 'rect' : 'tri';
    return {
      x: rand(0, W),
      y: -20,
      w: rand(6, 14),
      h: rand(8, 18),
      r: rand(0, Math.PI*2),
      rv: rand(-0.2, 0.2),
      vx: rand(-2, 2),
      vy: rand(2, 6),
      g: rand(0.06, 0.12),
      color: COLORS[(Math.random()*COLORS.length)|0],
      shape
    };
  }

  function drawPiece(p){
    ctx.save();
    ctx.translate(p.x, p.y);
    ctx.rotate(p.r);
    ctx.fillStyle = p.color;
    if (p.shape === 'rect') {
      ctx.fillRect(-p.w/2, -p.h/2, p.w, p.h);
    } else {
      ctx.beginPath();
      ctx.moveTo(0, -p.h/2);
      ctx.lineTo(p.w/2, p.h/2);
      ctx.lineTo(-p.w/2, p.h/2);
      ctx.closePath();
      ctx.fill();
    }
    ctx.restore();
  }

  function tick(){
    if (!running) return;
    ctx.clearRect(0,0,W,H);

    // Добавляем порциями для «взрыва»
    if (pieces.length < 300) {
      for (let i=0;i<10;i++) pieces.push(makePiece());
    }
    pieces.forEach(p=>{
      p.x += p.vx;
      p.y += p.vy;
      p.vy += p.g;
      p.r += p.rv;
    });
    pieces = pieces.filter(p => p.y < H + 50);
    pieces.forEach(drawPiece);

    requestAnimationFrame(tick);
  }

  function boom(){
    pieces = [];
    running = true;
    tick();
    // Автоостановка через 6 секунд
    setTimeout(()=>{ running=false; ctx.clearRect(0,0,W,H); }, 6000);
  }

  document.getElementById('ctaBtn').addEventListener('click', boom);
})();

/* === Показать кнопку «Выход» через 6 секунд, закрытие вкладки === */
setTimeout(()=>document.getElementById('exitBtn').classList.add('show'), 6000);
document.getElementById('exitBtn').addEventListener('click', async ()=>{
  try{
    if (document.fullscreenElement) await document.exitFullscreen();
  }catch(e){}
  // Попытка закрыть вкладку (удастся только при открытии скриптом или если браузер позволяет)
  window.close();
  // Фолбэк: очистить контент и перейти на about:blank
  setTimeout(()=>{ document.body.innerHTML=""; location.href="about:blank"; }, 200);
});
</script>
</body>
</html>
Если кому-то тоже интересно
вывод кода
вывод кода

Стиль интересный, как понял фотография до этого была другая? Надпись, наверное, лучше сверху сделать, а то у меня она почти не видно (увидеть в спойлере можно)

Да, была гармоничная, с подобранным к фону цветом, но увы archive.org удалил тот рисунок, видимо из подозрения на AI slop, поэтому сейчас пришлось поставить как заглушку аналогичный рисунок.

В остальном, код и стили те же, без изменений

Спасибо за оценку :). Рад, что поставилось.

Редко здесь бываю. Понятия не имею, как пользоваться LLM. С JC и остальными не знаком (на JC чего-то в коде скобки не "парятся"). Мне очень интересно, большое спасибо Автору.

Я вот такую штуку сделал https://thisman.github.io/my-valentine/
Маленькая игра, где до результата надо еще добраться

Спойлер финал этого проекта
конец
конец

Классно получилось, мне очень понравилось.

Из мелочей: с телефона не работает, чуть меньше бы фигуру и надпись скрыть до самого конца. Но идея отличная, а конец особенно порадовал, я люблю такие картинки)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Информация

Сайт
ruvds.com
Дата регистрации
Дата основания
Численность
11–30 человек
Местоположение
Россия
Представитель
ruvds