Рухомий рядок у додатках — що це, як створити та налаштувати

Автор: IT Sectr Опубліковано: 2026-07-25 Час читання: 9 хв

Рухомий рядок (marquee) — це візуальний ефект, за якого текст автоматично прокручується по горизонталі в межах обмеженої області, дозволяючи відображати довгі рядки в компактному контейнері. Історично термін походить від HTML-тега <marquee>, популярного у вебі 1990-х років. У сучасній мобільній розробці scrolling text використовується в плеєрах, новинних рядках і панелях статусу. Згідно з Apple Human Interface Guidelines і Google Material Design, рухомий рядок допустимий лише в обмежених сценаріях, де статичне обрізання з трьома крапками погіршує UX.

Головне

  • Рухомий рядок — автоматичне горизонтальне прокручування довгого тексту в обмеженому контейнері.
  • На Android реалізується через android:ellipsize="marquee" у TextView.
  • На iOS ефект робиться вручну через Core Animation або SwiftUI з анімацією offset.
  • У вебі HTML-тег <marquee> застарілий; використовується CSS-анімація з translateX.
  • Для accessibility обов’язкова підтримка prefers-reduced-motion.

Що таке рухомий рядок?

Рухомий рядок — це UI-ефект, за якого текст рухається по горизонталі всередині фіксованої області, циклічно прокручуючись, щоб користувач міг прочитати весь вміст, який не вміщується в контейнер. На відміну від ellipsize, який приховує частину тексту, marquee показує весь текст, але в русі.

Термін marquee (англ. — «шатро», «рекламний щит») закріпився завдяки HTML-тегу <marquee>, який був доданий у ранніх версіях браузерів Netscape та Internet Explorer. Тег дозволяв створювати прокручуваний текст без JavaScript і CSS, але ніколи не був частиною стандарту HTML. Сучасні браузери продовжують підтримувати його для зворотної сумісності, але використовувати його не рекомендується.

У мобільній розробці рухомий рядок вирішує проблему довгих підписів у компактних контейнерах: назви треків у плеєрі, курси валют, новинні рядки на телеекранах. Однак через анімацію вона створює навантаження на accessibility і може дратувати користувачів — тому застосовується вибірково.

За даними досліджень Nielsen Norman Group (2023), рухомий текст знижує швидкість читання на 15-20% і підвищує когнітивне навантаження. Використовуйте рухомий рядок лише тоді, коли статичне обрізання (ellipsize) дійсно неприйнятне.

Рухомий рядок на Android через TextView

На Android рухомий рядок реалізується вбудованим механізмом TextView через атрибут android:ellipsize="marquee". Це єдиний режим ellipsize, який не обрізає текст, а анімує його прокрутку.

Для активації marquee необхідно виконати три умови: singleLine="true" (або maxLines="1"), ellipsize="marquee" та setSelected(true) у коді. Без виклику setSelected() анімація не запуститься.

xml
<!-- XML layout -->
<TextView
    android:id="@+id/marquee_text"
    android:layout_width="150dp"
    android:layout_height="wrap_content"
    android:singleLine="true"
    android:ellipsize="marquee"
    android:marqueeRepeatLimit="marquee_forever" />

// Kotlin: activate marquee
textView.setSelected(true)
textView.isSingleLine = true

Атрибут marqueeRepeatLimit визначає кількість повторів анімації: marquee_forever (нескінченно) або конкретне число. Типово — marquee_forever. Якщо текст коротший за ширину контейнера, анімація не запускається — TextView показує статичний текст.

Важлива особливість: setSelected(true) працює лише коли TextView має фокус або перебуває в режимі selected. У RecyclerView це може викликати проблеми — анімація запускається лише для одного елемента. Рішення — використовувати кастомний MarqueeTextView, який перевизначає isFocused() і завжди повертає true.

Починаючи з Android 9 (API 28), Google покращила поведінку marquee — анімація стала плавнішою та не зависає при скролі списку. Однак на старих пристроях marquee може смикатися, особливо при високій щільності пікселів.

Реалізація scrolling text на iOS

У iOS немає вбудованого аналога android:ellipsize="marquee". Рухомий рядок реалізується вручну через Core Animation або за допомогою SwiftUI-анімації. Найпоширеніший підхід — анімація layer.position у поєднанні з кліпінг-контейнером.

У UIKit рухомий рядок будується на трьох елементах: контейнер UILabel з clipsToBounds = true, сам текст і CABasicAnimation для зсуву тексту по X з нескінченним повторенням та autoreverses = false, щоб рядок прокручувався в один бік і повертався стрибком.

swift
func startMarquee(for label: UILabel) {
    label.clipsToBounds = true
    label.numberOfLines = 1

    let textWidth = (label.text as? NSString)?
        .size(withAttributes: [.font: label.font as any]).width ?? 0

    guard textWidth > label.bounds.width else { return }

    let animation = CABasicAnimation(keyPath: "position.x")
    animation.fromValue = label.layer.position.x + label.bounds.width / 2
    animation.toValue = label.layer.position.x - textWidth - label.bounds.width / 2
    animation.duration = CFTimeInterval(textWidth / 60)
    animation.repeatCount = .infinity
    animation.autoreverses = false
    label.layer.add(animation, forKey: "marquee")
}

Розрахунок duration = textWidth / 60 дає швидкість приблизно 60 пікселів на секунду — комфортний темп для читання. Якщо швидкість вища за 120 px/s, текст стає нечитабельним. Якщо нижча за 30 px/s — анімація виглядає млявою. Змінюйте швидкість під конкретний сценарій.

У SwiftUI рухомий рядок реалізується простіше — через модифікатор offset() із repeatable анімацією та .animation(.linear(duration:).repeatForever(autoreverses: false)). SwiftUI автоматично керує життєвим циклом анімації, але вимагає GeometryReader для визначення ширини тексту.

CSS-анімація замість тега marquee

У вебі застарілий тег <marquee> не рекомендується до використання, хоча браузери продовжують його підтримувати. Сучасна реалізація рухомого рядка — CSS-анімація з ключовими кадрами та transform: translateX.

Переваги CSS-підходу: повний контроль над швидкістю, паузою та кривою анімації, підтримка prefers-reduced-motion для accessibility, відсутність семантично некоректного тега.

css
.marquee {
    overflow: hidden;
    white-space: nowrap;
    width: 300px;
}
.marquee-content {
    display: inline-block;
    animation: marquee 10s linear infinite;
}
@keyframes marquee {
    0%   { transform: translateX(0); }
    100% { transform: translateX(-100%); }
}

/* Pause on hover */
.marquee-content:hover {
    animation-play-state: paused;
}

Ключовий трюк: display: inline-block у контенту гарантує, що ширина елемента дорівнює ширині тексту, а translateX(-100%) зсуває його рівно на цю ширину вліво. Якщо контент display: block, translateX(-100%) дорівнюватиме ширині контейнера, а не тексту, і анімація зламається.

Для accessibility додайте @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } } — користувачі з вестибулярними розладами побачать статичний текст із можливістю скролу.

Коли використовувати та коли уникати

Рухомий рядок виправданий у сценаріях, де користувач не може самостійно розширити контейнер, а повний текст критично важливий: поточний трек у плеєрі на заблокованому екрані, новинний рядок на телевізійній панелі, рухомий рядок у додатку для таксі (статус замовлення).

Рухомий рядок не варто використовувати у таблицях, списках, формах і діалогах — усюди, де користувач активно взаємодіє з інтерфейсом. Анімація відволікає від мети, збільшує час пошуку елемента та створює дискомфорт. За даними WebAIM (2024), 1 із 3 користувачів із вестибулярними розладами повідомляє про дискомфорт під час перегляду анімованого тексту.

Правило великого пальця: якщо текст можна скоротити або помістити на наступний рядок — зробіть це. Рухомий рядок — крайній захід, а не універсальне рішення для довгих підписів.

Доступність: анімація та зменшений рух

Рухомий рядок — одна з найбільш проблемних UI-анімацій з точки зору accessibility. Рухомий текст може викликати запаморочення, нудоту та дезорієнтацію в людей із вестибулярними розладами. Згідно з WCAG 2.2, будь-яка анімація, яка триває більше 5 секунд, повинна мати механізм паузи або вимкнення.

На iOS використовуйте властивість UIAccessibility.isReduceMotionEnabled для перевірки системного налаштування. Якщо reduceMotion увімкнено — не запускайте анімацію, покажіть статичний текст із ellipsize.

На Android перевіряйте системну анімацію через Settings.Global.ANIMATOR_DURATION_SCALE. Якщо глобальна анімація вимкнена (duration scale = 0), рухомий рядок вмикати не потрібно.

У вебі використовуйте медіа-функцію prefers-reduced-motion: @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. Це стандартна CSS-можливість, підтримувана всіма сучасними браузерами. Перевірте в DevTools: увімкніть емуляцію prefers-reduced-motion і переконайтеся, що анімація вимикається.

Часто задавані питання

Чим відрізняється Marquee від Ellipsize?

Ellipsize обрізає текст і ставить три крапки, приховуючи частину вмісту. Marquee прокручує текст цілком, але в русі. Вибір залежить від пріоритету: показати все (marquee) або зберегти статику (ellipsize).

Як прискорити або сповільнити рухомий рядок на Android?

Вбудований android:ellipsize="marquee" не дозволяє змінювати швидкість — вона визначається системним ScrollSpeed. Для кастомної швидкості використовуйте Animation або ValueAnimator вручну.

Чому marquee не працює в RecyclerView?

Через перевикористання в’юх setSelected(true) скидається. Рішення: створіть кастомний MarqueeTextView, який перевизначає isFocused() і повертає true, або використовуйте кастомну анімацію в Adapter.onViewAttachedToWindow().

Чи можна зробити вертикальний рухомий рядок?

Так, через translateY замість translateX. Вертикальний marquee використовується для титрів (credits) або новинних рядків у вертикальних стрічках. Принцип той самий, але змінюється вісь анімації.

Як зупинити рухомий рядок при фокусі?

У CSS: :hover { animation-play-state: paused; }. На Android: відстежуйте фокус через OnFocusChangeListener і скидайте setSelected(false). На iOS: видаліть анімацію через layer.removeAnimation(forKey:).

Підсумки

  • Рухомий рядок — горизонтальне прокручування тексту, що показує вміст цілком за рахунок анімації.
  • На Android вбудований android:ellipsize="marquee" вимагає setSelected(true) і singleLine="true".
  • На iOS реалізація робиться через CABasicAnimation з ключовим шляхом position.x.
  • У вебі тег <marquee> застарілий; використовуйте CSS @keyframes з translateX.
  • Застосовуйте marquee лише коли ellipsize неприйнятний — у плеєрах, тікерах, статусних рядках.
  • Обов’язково вимикайте анімацію при prefers-reduced-motion на всіх платформах.
  • Швидкість анімації: ~60 px/s — комфортно для читання, вище 120 px/s — нечитабельно.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також