Teks berjalan (marquee) — adalah efek visual di mana teks secara otomatis bergulir secara horizontal di dalam area terbatas, memungkinkan menampilkan baris panjang dalam wadah kompak. Secara historis, istilah ini berasal dari tag HTML <marquee>, yang populer di web tahun 1990-an. Dalam pengembangan seluler modern, scrolling text digunakan di pemutar, ticker berita, dan bilah status. Menurut Apple Human Interface Guidelines dan Google Material Design, teks berjalan hanya diperbolehkan dalam skenario terbatas di mana pemotongan statis dengan elipsis memperburuk UX.
Poin utama
Teks berjalan — adalah efek UI di mana teks bergerak secara horizontal di dalam area tetap, bergulir secara siklik sehingga pengguna dapat membaca semua konten yang tidak muat di wadah. Tidak seperti ellipsize yang menyembunyikan sebagian teks, marquee menampilkan seluruh teks, tetapi dalam gerakan.
Istilah marquee (Inggris — “tenda”, “papan reklame”) menjadi mapan berkat tag HTML <marquee>, yang ditambahkan di versi awal browser Netscape dan Internet Explorer. Tag ini memungkinkan pembuatan teks bergulir tanpa JavaScript dan CSS, tetapi tidak pernah menjadi bagian dari standar HTML. Browser modern tetap mendukungnya untuk kompatibilitas mundur, tetapi penggunaannya tidak disarankan.
Dalam pengembangan seluler, teks berjalan memecahkan masalah label panjang dalam wadah kompak: nama lagu di pemutar, ticker nilai tukar, baris berita di layar TV. Namun, karena animasi, ia membebani accessibility dan dapat mengganggu pengguna — oleh karena itu diterapkan secara terbatas.
Menurut penelitian Nielsen Norman Group (2023), teks bergerak mengurangi kecepatan membaca sebesar 15-20% dan meningkatkan beban kognitif. Gunakan teks berjalan hanya ketika pemotongan statis (ellipsize) benar-benar tidak dapat diterima.
Di Android, teks berjalan diimplementasikan melalui mekanisme bawaan TextView dengan atribut android:ellipsize="marquee". Ini adalah satu-satunya mode ellipsize yang tidak memotong teks, tetapi menganimasikan penggulirannya.
Untuk mengaktifkan marquee, tiga kondisi harus dipenuhi: singleLine="true" (atau maxLines="1"), ellipsize="marquee" dan setSelected(true) di kode. Tanpa panggilan setSelected() animasi tidak akan dimulai.
<!-- 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: aktifkan marquee
textView.setSelected(true)
textView.isSingleLine = true
Atribut marqueeRepeatLimit menentukan jumlah pengulangan animasi: marquee_forever (tak terbatas) atau angka tertentu. Default — marquee_forever. Jika teks lebih pendek dari lebar wadah, animasi tidak dimulai — TextView menampilkan teks statis.
Fitur penting: setSelected(true) hanya berfungsi ketika TextView memiliki fokus atau dalam mode selected. Di RecyclerView ini dapat menyebabkan masalah — animasi hanya dimulai untuk satu elemen. Solusi — gunakan MarqueeTextView kustom yang menimpa isFocused() dan selalu mengembalikan true.
Mulai Android 9 (API 28), Google meningkatkan perilaku marquee — animasi menjadi lebih halus dan tidak macet saat menggulir daftar. Namun, di perangkat lama marquee mungkin tersendat, terutama pada kepadatan piksel tinggi.
Di iOS tidak ada padanan bawaan untuk android:ellipsize="marquee". Teks berjalan diimplementasikan secara manual melalui Core Animation atau dengan animasi SwiftUI. Pendekatan paling umum — animasi layer.position dalam kombinasi dengan wadah clipping.
Di UIKit, teks berjalan dibangun di atas tiga elemen: wadah UILabel dengan clipsToBounds = true, teks itu sendiri, dan CABasicAnimation untuk menggeser teks sepanjang X dengan pengulangan tak terbatas dan autoreverses = false, sehingga bilah bergulir dalam satu arah dan kembali dengan lompatan.
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")
}
Perhitungan duration = textWidth / 60 memberikan kecepatan sekitar 60 piksel per detik — tempo nyaman untuk membaca. Jika kecepatan di atas 120 px/s, teks menjadi tidak terbaca. Jika di bawah 30 px/s — animasi terlihat lamban. Ubah speed sesuai skenario spesifik.
Di SwiftUI, teks berjalan diimplementasikan lebih sederhana — melalui pengubah offset() dengan animasi berulang dan .animation(.linear(duration:).repeatForever(autoreverses: false)). SwiftUI secara otomatis mengelola siklus hidup animasi, tetapi memerlukan GeometryReader untuk menentukan lebar teks.
Di web, tag usang <marquee> tidak disarankan untuk digunakan, meskipun browser tetap mendukungnya. Implementasi modern teks berjalan — animasi CSS dengan bingkai kunci dan transform: translateX.
Kelebihan pendekatan CSS: kontrol penuh atas kecepatan, jeda, dan kurva animasi, dukungan prefers-reduced-motion untuk accessibility, tidak adanya tag yang salah secara semantik.
.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;
}
Trik utama: display: inline-block pada konten memastikan lebar elemen sama dengan lebar teks, dan translateX(-100%) menggesernya tepat selebar itu ke kiri. Jika konten memiliki display: block, translateX(-100%) akan sama dengan lebar wadah, bukan lebar teks, dan animasi akan rusak.
Untuk accessibility, tambahkan @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } } — pengguna dengan gangguan vestibular akan melihat teks statis dengan kemampuan menggulir.
Teks berjalan dibenarkan dalam skenario di mana pengguna tidak dapat memperbesar wadah sendiri dan teks lengkap sangat penting: lagu saat ini di pemutar di layar terkunci, baris berita di panel TV, teks berjalan di aplikasi taksi (status pesanan).
Teks berjalan sebaiknya tidak digunakan di tabel, daftar, formulir, dan dialog — di mana pun pengguna aktif berinteraksi dengan antarmuka. Animasi mengalihkan perhatian, meningkatkan waktu pencarian elemen, dan menyebabkan ketidaknyamanan. Menurut WebAIM (2024), 1 dari 3 pengguna dengan gangguan vestibular melaporkan ketidaknyamanan saat melihat teks animasi.
Aturan praktis: jika teks dapat dipersingkat atau ditempatkan di baris berikutnya — lakukan itu. Teks berjalan adalah upaya terakhir, bukan solusi universal untuk label panjang.
Teks berjalan adalah salah satu animasi UI yang paling bermasalah dari segi accessibility. Teks bergerak dapat menyebabkan pusing, mual, dan disorientasi pada orang dengan gangguan vestibular. Menurut WCAG 2.2, setiap animasi yang berlangsung lebih dari 5 detik harus memiliki mekanisme jeda atau penonaktifan.
Di iOS, gunakan properti UIAccessibility.isReduceMotionEnabled untuk memeriksa pengaturan sistem. Jika reduceMotion diaktifkan — jangan jalankan animasi, tampilkan teks statis dengan ellipsize.
Di Android, periksa animasi sistem melalui Settings.Global.ANIMATOR_DURATION_SCALE. Jika animasi global dinonaktifkan (duration scale = 0), teks berjalan tidak boleh diaktifkan.
Di web, gunakan fungsi media prefers-reduced-motion: @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. Ini adalah kemampuan CSS standar yang didukung oleh semua browser modern. Periksa di DevTools: aktifkan emulasi prefers-reduced-motion dan pastikan animasi dinonaktifkan.
Pertanyaan yang sering diajukan
Ellipsize memotong teks dan menempatkan elipsis, menyembunyikan sebagian konten. Marquee menggulir seluruh teks, tetapi dalam gerakan. Pilihan tergantung pada prioritas: menampilkan semuanya (marquee) atau mempertahankan statis (ellipsize).
android:ellipsize="marquee" bawaan tidak mengizinkan perubahan kecepatan — ditentukan oleh ScrollSpeed sistem. Untuk kecepatan kustom, gunakan Animation atau ValueAnimator secara manual.
Karena penggunaan ulang view, setSelected(true) di-reset. Solusi: buat MarqueeTextView kustom yang menimpa isFocused() dan mengembalikan true, atau gunakan animasi kustom di Adapter.onViewAttachedToWindow().
Ya, melalui translateY sebagai pengganti translateX. Marquee vertikal digunakan untuk kredit (credits) atau baris berita di jalur vertikal. Prinsipnya sama, tetapi sumbu animasi berubah.
Di CSS: :hover { animation-play-state: paused; }. Di Android: lacak fokus melalui OnFocusChangeListener dan reset setSelected(false). Di iOS: hapus animasi melalui layer.removeAnimation(forKey:).
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga