Как реализовать последовательную плавную прокрутку нескольких блоков на странице, при которой прокрутка автоматически переходит от одного блока к следующему?

Всем привет! <br/> Прошу помощи в реализации последовательной прокрутки нескольких блоков — предполагаю использовать GSAP, но пробованные варианты не дали нужного результата. Основная проблема: GSAP в любом случае продолжает прокручивать всю страницу, а мне это не подходит. <br/> <b>Структура:</b> <br/> Две строки, в каждой по две колонки: в левой — изображение, в правой — контент. Высота контента правой колонки больше высоты строки. <br/> <b>Логика:</b> <br/> При прокрутке страницы в обратном направлении анимации должны проигрываться в обратном порядке. <br/> <b>Структура и минимальные стили <a href="https://jsfiddle.net/zahar_92/bjxuo8s6/34/" rel="nofollow">https://jsfiddle.net/zahar_92/bjxuo8s6/34/</a></b>
  • 13 февраля 2026 г. 10:11
Ответы на вопрос 1
Коротко — задача ложится идеально на ScrollTrigger от GSAP: для каждого ряда (“panel”) делаем «пин» секции на время, равное разнице высот внутреннего контента и видимой секции, и параллельно анимируем смещение внутреннего контента вверх (scrub: true). При обратной прокрутке всё проигрывается в обратном порядке автоматически.

Ниже минимальный рабочий пример (GSAP 3 + ScrollTrigger). Подставьте свои селекторы/HTML — идея та же.

HTML (структура):
- .panel — одна строка (высота = 100vh)
- в ней .left и .right
- в правой колонке .content-inner — блок с большей высотой

CSS (минимум):
- .panel { display:flex; height:100vh; overflow:hidden; }
- .right { flex:1; overflow:hidden; position:relative; }
- .content-inner { position:relative; }

JS (GSAP + ScrollTrigger):
1) подключите gsap и ScrollTrigger
2) выполните:

gsap.registerPlugin(ScrollTrigger);

document.querySelectorAll('.panel').forEach(panel => {
  const content = panel.querySelector('.content-inner');
  if (!content) return;

  // delta — насколько контент больше видимой панели
  const delta = content.scrollHeight - panel.clientHeight;

  // если контент не превышает высоту панели — ничего не делаем
  if (delta <= 0) return;

  gsap.to(content, {
    y: -delta,              // сдвигаем контент вверх на разницу
    ease: 'none',
    scrollTrigger: {
      trigger: panel,
      start: 'top top',     // когда верх панели вверху вьюпорта — pin
      end: () => `+=${delta}`, // протяжённость прокрутки равна delta
      scrub: true,          // синхронизировано со скроллом (включая обратный)
      pin: true,            // "фиксируем" секцию на время анимации
      anticipatePin: 1,
      // markers: true      // включите на время отладки
    }
  });
});

Объяснение почему это работает
- pin: true удерживает текущую секцию вверху экрана, поэтому страница «не пролистывает дальше», пока не проиграется анимация — именно этого вы хотели (а не «продолжения обычного скролла страницы»).
- scrub: true делает анимацию синхронной со скроллом и автоматически воспроизводит её в обратном порядке при прокрутке назад.
- end вычисляется как delta — то есть сколько пикселей прокрутки нужно, чтобы полностью показать скрытую часть контента.

Полезные советы / подводные камни
- Убедитесь, что .panel имеет фиксированную видимую высоту (обычно 100vh). Если панель сама растягивается, расчёт delta будет неверный.
- Если вы используете относительные единицы (vh) или динамичный контент — вызывайте ScrollTrigger.refresh() на resize / после загрузки изображений.
- Для отладки включите markers: true.
- Если у вас несколько подобных секций последовательно — они автоматически будут проигрываться одна за другой.
- Для адаптивности используйте ScrollTrigger.matchMedia, чтобы отключать пин/анимацию на мобильных экранах при необходимости.

Если хотите, могу конкретно подправить ваш jsfiddle (вставить код прямо туда) — пришлите ссылку ещё раз или дайте доступ, и я внесу правки.
Похожие вопросы