Il testo scorrevole (marquee) è un effetto visivo in cui il testo scorre automaticamente orizzontalmente all'interno di un'area limitata, consentendo di visualizzare stringhe lunghe in un contenitore compatto. Storicamente, il termine deriva dal tag HTML <marquee>, popolare sul web negli anni '90. Nello sviluppo mobile moderno, lo scrolling text viene utilizzato in lettori, ticker di notizie e barre di stato. Secondo le Apple Human Interface Guidelines e Google Material Design, il marquee è accettabile solo in scenari limitati in cui il troncamento statico con puntini di sospensione degrada l'UX.
Punti chiave
Il testo scorrevole è un effetto UI in cui il testo si muove orizzontalmente all'interno di un'area fissa, scorrendo ciclicamente in modo che l'utente possa leggere tutto il contenuto che non entra nel contenitore. A differenza di ellipsize, che nasconde parte del testo, marquee mostra l'intero testo, ma in movimento.
Il termine marquee si è affermato grazie al tag HTML <marquee>, che è stato aggiunto nelle prime versioni dei browser Netscape e Internet Explorer. Il tag permetteva di creare testo scorrevole senza JavaScript o CSS, ma non è mai stato parte dello standard HTML. I browser moderni continuano a supportarlo per compatibilità con le versioni precedenti, ma il suo uso non è raccomandato.
Nello sviluppo mobile, il marquee risolve il problema delle etichette lunghe in contenitori compatti: nomi di tracce in un lettore, ticker di valute, notizie su schermi TV. Tuttavia, a causa dell'animazione, crea un carico sull'accessibilità e può infastidire gli utenti — quindi viene usato in modo selettivo.
Secondo una ricerca del Nielsen Norman Group (2023), il testo in movimento riduce la velocità di lettura del 15-20% e aumenta il carico cognitivo. Usa marquee solo quando il troncamento statico (ellipsize) è veramente inaccettabile.
Su Android, il marquee è implementato dal meccanismo integrato di TextView tramite l'attributo android:ellipsize="marquee". Questa è l'unica modalità ellipsize che non tronca il testo ma ne anima lo scorrimento.
Per attivare il marquee, devono essere soddisfatte tre condizioni: singleLine="true" (o maxLines="1"), ellipsize="marquee" e setSelected(true) nel codice. Senza chiamare setSelected(), l'animazione non partirà.
<!-- 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
L'attributo marqueeRepeatLimit determina il numero di ripetizioni dell'animazione: marquee_forever (infinito) o un numero specifico. Il valore predefinito è marquee_forever. Se il testo è più corto della larghezza del contenitore, l'animazione non parte — TextView mostra testo statico.
Una caratteristica importante: setSelected(true) funziona solo quando TextView ha il focus o è in modalità selezionata. In RecyclerView, ciò può causare problemi — l'animazione parte solo per un elemento. La soluzione è creare un MarqueeTextView personalizzato che sovrascriva isFocused() e restituisca sempre true.
A partire da Android 9 (API 28), Google ha migliorato il comportamento del marquee — l'animazione è diventata più fluida e non si blocca durante lo scorrimento di una lista. Tuttavia, sui dispositivi più vecchi, il marquee può sussultare, specialmente ad alte densità di pixel.
iOS non ha un equivalente integrato di android:ellipsize="marquee". Il marquee è implementato manualmente utilizzando Core Animation o l'animazione SwiftUI. L'approccio più comune è animare layer.position in combinazione con un contenitore di clipping.
In UIKit, il marquee è costruito con tre elementi: un contenitore UILabel con clipsToBounds = true, il testo stesso e una CABasicAnimation per spostare il testo sull'asse X con ripetizione infinita e autoreverses = false, in modo che la riga scorra in una direzione e torni con un salto.
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")
}
Calcolare duration = textWidth / 60 dà una velocità di circa 60 pixel al secondo — un ritmo di lettura confortevole. Se la velocità supera 120 px/s, il testo diventa illeggibile. Se è inferiore a 30 px/s, l'animazione sembra lenta. Regola la velocità per il tuo scenario specifico.
In SwiftUI, il marquee è implementato più semplicemente — tramite il modificatore offset() con animazione ripetibile e .animation(.linear(duration:).repeatForever(autoreverses: false)). SwiftUI gestisce automaticamente il ciclo di vita dell'animazione ma richiede GeometryReader per determinare la larghezza del testo.
Sul web, il tag obsoleto <marquee> è sconsigliato, sebbene i browser continuino a supportarlo. L'implementazione moderna del marquee è l'animazione CSS con keyframes e transform: translateX.
Vantaggi dell'approccio CSS: controllo completo su velocità, pausa e curva di animazione, supporto di prefers-reduced-motion per l'accessibilità e assenza di un tag semanticamente scorretto.
.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;
}
Il trucco fondamentale: display: inline-block sul contenuto garantisce che la larghezza dell'elemento sia uguale alla larghezza del testo, e translateX(-100%) lo sposta esattamente di quella larghezza verso sinistra. Se il contenuto è display: block, translateX(-100%) sarà uguale alla larghezza del contenitore, non a quella del testo, e l'animazione si romperà.
Per l'accessibilità, aggiungi @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } } — gli utenti con disturbi vestibolari vedranno testo statico con possibilità di scorrimento.
Il marquee è giustificato in scenari in cui l'utente non può espandere il contenitore da solo e il testo completo è criticamente importante: la traccia corrente in un lettore su schermo bloccato, un ticker di notizie su un pannello TV o un marquee in un'app di ride-hailing (stato dell'ordine).
Il marquee non deve essere usato in tabelle, elenchi, moduli e dialoghi — ovunque l'utente interagisca attivamente con l'interfaccia. L'animazione distrae dall'obiettivo, aumenta il tempo di ricerca degli elementi e crea disagio. Secondo WebAIM (2024), 1 utente su 3 con disturbi vestibolari segnala disagio durante la visualizzazione di testo animato.
Una regola pratica: se il testo può essere abbreviato o portato alla riga successiva — fallo. Il marquee è un'ultima risorsa, non una soluzione universale per etichette lunghe.
Il marquee è una delle animazioni UI più problematiche dal punto di vista dell'accessibilità. Il testo in movimento può causare vertigini, nausea e disorientamento nelle persone con disturbi vestibolari. Secondo WCAG 2.2, qualsiasi animazione che duri più di 5 secondi deve avere un meccanismo di pausa o disattivazione.
Su iOS, usa la proprietà UIAccessibility.isReduceMotionEnabled per verificare l'impostazione di sistema. Se reduceMotion è attivato — non avviare l'animazione, mostra testo statico con ellipsize.
Su Android, verifica l'animazione di sistema tramite Settings.Global.ANIMATOR_DURATION_SCALE. Se l'animazione globale è disattivata (duration scale = 0), il marquee non deve essere attivato.
Sul web, usa la funzione media prefers-reduced-motion: @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. Questa è una funzionalità CSS standard supportata da tutti i browser moderni. Verifica in DevTools: attiva l'emulazione di prefers-reduced-motion e conferma che l'animazione si disattivi.
Domande frequenti
Ellipsize tronca il testo e aggiunge puntini di sospensione, nascondendo parte del contenuto. Marquee scorre l'intero testo, ma in movimento. La scelta dipende dalla priorità: mostrare tutto (marquee) o mantenere la visualizzazione statica (ellipsize).
Il android:ellipsize="marquee" integrato non permette di cambiare la velocità — è determinata dallo ScrollSpeed di sistema. Per una velocità personalizzata, usa Animation o ValueAnimator manualmente.
A causa del riciclo delle viste, setSelected(true) viene reimpostato. Soluzione: crea un MarqueeTextView personalizzato che sovrascriva isFocused() e restituisca true, o usa un'animazione personalizzata in Adapter.onViewAttachedToWindow().
Sì, usando translateY invece di translateX. Il marquee verticale è usato per i crediti o i ticker di notizie nei feed verticali. Il principio è lo stesso, ma l'asse dell'animazione cambia.
In CSS: :hover { animation-play-state: paused; }. Su Android: monitora il focus tramite OnFocusChangeListener e reimposta setSelected(false). Su iOS: rimuovi l'animazione tramite layer.removeAnimation(forKey:).
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche