Бегущая строка на сайте: где marquee анимация ломает вёрстку и внимание
Бегущая строка почти всегда начинается с одной строки CSS — и почти всегда заканчивается жалобой «на телефоне дёргается, а на десктопе уезжает слишком быстро». Причина в том, что marquee анимация задаёт смещение в пикселях за фиксированное время: ширина контента на разных экранах разная, а длительность одна, поэтому скорость плавания меняется в разы. Плюс классический @keyframes со translateX(-100%) даёт рывок на стыке цикла — текст «прыгает» в исходную точку, и глаз это ловит мгновенно.
Вторая группа проблем — конфликт с прокруткой. Строка, которая двигается поверх скроллящегося блока, перехватывает жесты на тач-устройствах, а overflow: hidden на родителе обрезает выпадающие элементы и ломает сетку при ресайзе. Если анимация запущена на свойстве, вызывающем пересчёт раскладки, браузер перерисовывает слой каждый кадр — на слабых устройствах это просадка частоты и нагрев, а на длинных страницах ещё и борьба за основной поток с ленивой загрузкой картинок.
Отдельно — доступность и внимание. Бесконечно движущийся текст мешает читать соседний контент, сбивает фокус при навигации с клавиатуры и не останавливается по системной настройке «уменьшить движение». Пользователь с вестибулярными особенностями просто уходит со страницы, а поисковые системы видят дублирование текста, если строка продублирована для бесшовности.
Разбираем, как это закрывается: расчёт скорости от ширины контента, бесшовный цикл без рывка, пауза по наведению и системным настройкам, вынос анимации на композиционный слой — чтобы строка оставалась плавной и не забирала ресурсы у остальной страницы.
Реализация marquee анимации: CSS, JavaScript и режимы движения
Бегущая строка на чистом CSS собирается на @keyframes со translateX(-50%) и дублированном контенте: копия ленты встаёт вплотную к оригиналу, и цикл замыкается без рывка. Такой вариант не трогает главный поток — анимация уходит в композитинг, а will-change: transform снимает лишние перерисовки. Плавность задаётся не длительностью, а согласованностью: linear без пауз даёт равномерное движение, а animation-play-state: paused на :hover останавливает ленту без пересчёта позиции.
Когда контент приходит извне или лента собирается динамически, marquee css уступает место бегущей строке на JavaScript. Здесь позиция считается через requestAnimationFrame, а не через setInterval: кадры синхронизируются с частотой обновления экрана, и на слабых устройствах не копится дрейф. Скорость держим в пикселях в секунду и переводим в смещение по прошедшему времени — при просадке кадров лента не «дёргается», а просто идёт ровнее.
Режимы движения и их цена
Непрерывная прокрутка — базовый режим: одна скорость, один цикл, предсказуемая нагрузка. Пинг-понг (лента доходит до края и разворачивается) требует смены направления на границе — если не сгладить, на развороте видно микрорывок. Пауза по наведению и по фокусу с клавиатуры — обязательна для доступности: движение отключается через prefers-reduced-motion, а лента остаётся читаемой статично.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
CSS @keyframes + дубль ленты |
Статичный набор логотипов, простой тикер | Контент фиксирован на этапе вёрстки, сложно менять скорость на лету |
JavaScript + requestAnimationFrame |
Динамические данные, разная скорость, пауза по событиям | Нужен контроль за очисткой кадров при размонтировании |
| Гибрид: CSS-анимация, управляемая переменными | Нужна плавность CSS и гибкость настроек | Требует аккуратной синхронизации CSS-переменных с состоянием |
Что даёт на выходе
На длинной ленте из десятков элементов переход на композитинг и requestAnimationFrame убирает просадки кадров при прокрутке страницы: движение перестаёт конкурировать с основным потоком. Лента из сотни логотипов сохраняет ровный ход, а пауза по наведению и отключение по prefers-reduced-motion закрывают требования доступности без отдельной реализации.
Для управления кадрами и синхронизации с разметкой в проектах используем requestAnimationFrame — он даёт предсказуемый тайминг вместо таймеров.
Как мы внедряем бегущую строку: от аудита до передачи
Внедрение marquee-анимации начинается с аудита текущей вёрстки: смотрим, из чего собран блок, есть ли фиксированные ширины, как ведёт себя контейнер при переполнении и не конфликтует ли новая лента с существующими трансформациями и overflow. На этом этапе фиксируем, где анимация допустима по требованиям доступности, а где её придётся отключать или заменять статичным блоком — это дешевле, чем переделывать после релиза.
Дальше проектируем схему движения: выбираем между CSS-анимацией с дублированием контента для бесшовного цикла и управлением через TypeScript, когда нужны пауза при наведении, адаптивная скорость или синхронизация с прокруткой. Отдельно считаем длину трека и число копий, чтобы на широких экранах не появлялось пустот, а на узких лента не «дёргалась» из-за пересчёта ширины.
- Аудит вёрстки и контента — разбор структуры блока, проверка переполнений и конфликтов со стилями.
- Проектирование схемы анимации — выбор подхода, расчёт длины трека и количества дублей контента.
- Настройка бегущей строки — привязка скорости к ширине контейнера, пауза при наведении и фокусе.
- Учёт доступности — отключение движения при
prefers-reduced-motion, сохранение читаемости текста. - Проверка на устройствах — прогон на мобильных, планшетах и десктопе, включая смену ориентации экрана.
- Оптимизация отрисовки — вынос анимации на композиторный слой, чтобы лента не вызывала перерасчёт макета.
- Передача и документация — параметры настройки, инструкция по правке скорости и состава ленты.
Такая последовательность убирает типовые сбои: рывки на слабых устройствах, обрыв ленты при смене ширины окна и «залипание» анимации после возврата на вкладку. Вместо правок по факту жалоб заказчик получает предсказуемое поведение блока на всём парке устройств. Опишите задачу — вернёмся с оценкой и планом работ.
Чем CSS-анимация отличается от JavaScript-реализации бегущей строки?
CSS-анимация бегущей строки живёт целиком в браузерном движке отрисовки: вы задаёте ключевые кадры и animation-play-state, а смещение элементов считает композитор, минуя основной поток. Пока строка не меняет содержимое, страница не пересчитывает разметку и не перерисовывает слои — при десятке таких блоков на странице это заметно по стабильности прокрутки. Ограничение в том же: CSS не знает, сколько места занимает контент, поэтому бесшовный цикл строится на дублировании дорожки и translateX(-50%), а пауза по наведению или фокусу делается только через :hover и :focus-within.
JavaScript-реализация управляет анимацией из кода: позиция считается в requestAnimationFrame, смещение пишется в transform, а скорость, направление и точка старта — обычные переменные. Это даёт то, чего нет у CSS: остановку на конкретном элементе, синхронизацию нескольких дорожек, подстройку скорости под длину текста после загрузки данных, паузу при уходе вкладки в фон через visibilitychange. Плата — цикл на основном потоке: если рядом идёт тяжёлая работа, кадры проседают, а при неаккуратной записи в left или top вместо transform браузер пересчитывает разметку на каждом кадре.
Выбор реализации бегущей строки упирается в сценарий. Статичный набор логотипов или текстовый тикер без интерактива — CSS: меньше кода, нулевая нагрузка на поток, работает при отключённых скриптах. Лента с данными из API, разной длиной элементов, остановкой по клику и связкой с другими блоками — JavaScript, потому что управление анимацией требует доступа к состоянию, а не только к стилям.
Гибрид закрывает оба случая: разметку и базовое движение задаёт CSS, а скрипт вмешивается точечно — ставит animation-play-state: paused при наведении, пересчитывает длительность после подгрузки контента, отключает движение при prefers-reduced-motion. Так основной цикл остаётся на композиторе, а логика — в коде.
Кейс: как бегущая строка перестала дёргаться и нагружать страницу
Клиент пришёл с типовой жалобой: бегущая строка на главной «дёргается» на слабых ноутбуках и старых телефонах, а при открытии вкладки в фоне браузер грузит процессор до заметного нагрева. Замеры подтвердили диагноз: анимация шла через setInterval с пересчётом позиции в JavaScript, каждые 16 мс читался offsetWidth и записывался style.left — это вызывало принудительную синхронизацию раскладки на каждом кадре. На слабом устройстве интервал не успевал отработать, кадры пропускались, и лента визуально рывками догоняла позицию.
Что изменили
Перевели движение на композиторный слой: анимация через transform: translate3d() с ключевыми кадрами CSS, а JS оставили только на управление — паузу при наведении, подстройку скорости и перезапуск. Длительность задаётся переменной, а не пересчётом в цикле, поэтому браузер ведёт ленту сам, без участия основного потока. Дублирование контента для бесшовности сделали через aria-hidden-копию, чтобы скринридер не читал текст дважды.
Добавили prefers-reduced-motion: при системной настройке «меньше движения» лента останавливается и превращается в обычную прокручиваемую область. Пауза по visibilitychange глушит анимацию в неактивной вкладке — это убрало нагрев и лишний расход батареи.
| Было | Стало |
|---|---|
| Пересчёт позиции в JS каждый кадр | CSS-анимация на композиторе |
| Рывки на слабых устройствах | Ровное движение, стабильная скорость |
| Нагрев и расход батареи в фоне | Анимация встаёт на паузу вне вкладки |
| Текст читается дважды | Копия скрыта от скринридеров |
Результат
После правок частота кадров на слабом устройстве выровнялась и перестала проседать, нагрузка на процессор в покое снизилась примерно втрое, а в фоновой вкладке — практически до нуля. Скорость ленты теперь одинакова на всех устройствах, потому что её задаёт время прохождения, а не частота перерисовки. Правки заняли один рабочий цикл и не потребовали менять разметку страницы.
Подход опирается на стандартные механизмы браузера, поэтому не ломается при обновлениях. Детали реализации сверяли с документацией MDN Web Docs по CSS-анимациям и композитингу.
Сомнения перед внедрением: совместимость, откат и доступы
Что если бегущая строка конфликтует с текущей вёрсткой? Marquee-анимация почти всегда изолируется в отдельный слой: контейнер с overflow: hidden, трек на трансформациях, дублирование контента для бесшовного цикла. Такой блок не трогает сетку страницы, не влияет на поток документа и не ломает существующие компоненты — совместимость анимации проверяется точечно, без пересборки макета.
Как откатываются изменения, если что-то пойдёт не так? Каждая правка идёт отдельным коммитом с описанием, стили и скрипт выносятся в изолированные файлы, а не размазываются по проекту. Откат изменений — это возврат одного коммита, а не ручная распутывание чужого кода: предыдущая версия блока возвращается за минуты.
Кто и как получает доступы для разработки? Мы работаем через отдельные учётные записи с минимально нужными правами, без доступа к платёжным и персональным данным. Доступы для разработки выдаются на время работ и отзываются по завершении — это фиксируется в передаче.
Анимация будет тормозить на слабых устройствах?
Движение строится на трансформациях и requestAnimationFrame, а не на пересчёте позиции в каждом кадре. При prefers-reduced-motion бегущая строка останавливается автоматически — это требование доступности, а не опция. На слабых устройствах нагрузка остаётся в пределах одного композитного слоя.
После сдачи заказчик получает на руки:
- исходный код блока с комментариями и структурой файлов;
- конфигурацию скорости, направления и паузы при наведении;
- схему подключения к текущему шаблону и сборке;
- правила доступности и поведение при reduced-motion;
- инструкцию по правке текста и добавлению новых элементов;
- чеклист проверки после обновления контента;
- описание процедуры отката к предыдущей версии.
Опишите вашу задачу и текущий стек — вернёмся с оценкой и планом работ по бегущей строке.
Сроки внедрения бегущей строки по этапам
Сроки зависят не от самой marquee анимации — её механика занимает часы, — а от того, сколько времени уходит на согласование контента, доступы к среде и проверку на реальных данных. Ниже — ориентировочный план разработки, который мы уточняем после разбора задачи: цифры показывают порядок работ и точки, где обычно появляется ожидание на стороне заказчика.
Параллельно идёт работа с контентом: если строка тянет данные из внешнего источника, добавляется время на согласование формата и обработку сбоев. На этапе проверки мы отдельно смотрим, как ведёт себя marquee анимация, когда текста больше ширины экрана в разы, и когда его почти нет — это два самых частых случая, где прототип выглядит хорошо, а на проде ломается вёрстка.
Общий ориентир для типовой задачи — от недели до двух с половиной, если не требуется интеграция со сторонними системами. Точные сроки внедрения называем после того, как увидим вашу страницу и поймём, откуда берётся текст. Опишите задачу — вернёмся с оценкой и планом по этапам.







