El texto deslizante (marquee) es un efecto visual en el que el texto se desplaza automáticamente de forma horizontal dentro de un área limitada, permitiendo mostrar cadenas largas en un contenedor compacto. Históricamente, el término proviene de la etiqueta HTML <marquee>, popular en la web de los años 1990. En el desarrollo móvil moderno, el scrolling text se utiliza en reproductores, tickers de noticias y barras de estado. Según las Apple Human Interface Guidelines y Google Material Design, el marquee solo es aceptable en escenarios limitados donde el truncamiento estático con puntos suspensivos degrada la UX.
Puntos clave
El texto deslizante es un efecto de UI en el que el texto se mueve horizontalmente dentro de un área fija, desplazándose cíclicamente para que el usuario pueda leer todo el contenido que no cabe en el contenedor. A diferencia de ellipsize, que oculta parte del texto, marquee muestra el texto completo, pero en movimiento.
El término marquee se consolidó gracias a la etiqueta HTML <marquee>, que se agregó en las primeras versiones de los navegadores Netscape e Internet Explorer. La etiqueta permitía crear texto desplazable sin JavaScript ni CSS, pero nunca fue parte del estándar HTML. Los navegadores modernos siguen soportándola por compatibilidad hacia atrás, pero no se recomienda su uso.
En el desarrollo móvil, el marquee resuelve el problema de las etiquetas largas en contenedores compactos: nombres de canciones en un reproductor, tickers de divisas, noticias en pantallas de TV. Sin embargo, debido a la animación, supone una carga para la accesibilidad y puede molestar a los usuarios, por lo que se utiliza de forma selectiva.
Según la investigación de Nielsen Norman Group (2023), el texto en movimiento reduce la velocidad de lectura entre un 15 y un 20% y aumenta la carga cognitiva. Use marquee solo cuando el truncamiento estático (ellipsize) sea realmente inaceptable.
En Android, el marquee se implementa mediante el mecanismo integrado de TextView a través del atributo android:ellipsize="marquee". Este es el único modo ellipsize que no trunca el texto, sino que anima su desplazamiento.
Para activar marquee deben cumplirse tres condiciones: singleLine="true" (o maxLines="1"), ellipsize="marquee" y setSelected(true) en código. Sin llamar a setSelected(), la animación no se iniciará.
<!-- 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
El atributo marqueeRepeatLimit determina el número de repeticiones de la animación: marquee_forever (infinito) o un número específico. El valor predeterminado es marquee_forever. Si el texto es más corto que el ancho del contenedor, la animación no se inicia — TextView muestra texto estático.
Una característica importante: setSelected(true) solo funciona cuando TextView tiene el foco o está en modo seleccionado. En RecyclerView, esto puede causar problemas: la animación solo se inicia para un elemento. La solución es usar un MarqueeTextView personalizado que sobrescriba isFocused() y siempre devuelva true.
A partir de Android 9 (API 28), Google mejoró el comportamiento de marquee: la animación se volvió más fluida y no se congela al desplazar una lista. Sin embargo, en dispositivos antiguos, marquee puede entrecortarse, especialmente en densidades de píxeles altas.
iOS no tiene un equivalente integrado de android:ellipsize="marquee". El marquee se implementa manualmente mediante Core Animation o animación SwiftUI. El enfoque más común es animar layer.position combinado con un contenedor de recorte.
En UIKit, el marquee se construye con tres elementos: un contenedor UILabel con clipsToBounds = true, el texto mismo y una CABasicAnimation para desplazar el texto en X con repetición infinita y autoreverses = false, para que la línea se desplace en una dirección y vuelva de 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")
}
Calcular duration = textWidth / 60 da una velocidad de aproximadamente 60 píxeles por segundo, un ritmo de lectura cómodo. Si la velocidad supera los 120 px/s, el texto se vuelve ilegible. Si es inferior a 30 px/s, la animación parece lenta. Ajuste la velocidad según su escenario específico.
En SwiftUI, el marquee se implementa de forma más sencilla: mediante el modificador offset() con animación repetible y .animation(.linear(duration:).repeatForever(autoreverses: false)). SwiftUI gestiona automáticamente el ciclo de vida de la animación, pero requiere GeometryReader para determinar el ancho del texto.
En la web, la obsoleta etiqueta <marquee> no se recomienda, aunque los navegadores siguen soportándola. La implementación moderna del marquee es la animación CSS con keyframes y transform: translateX.
Ventajas del enfoque CSS: control total sobre la velocidad, pausa y curva de animación, soporte de prefers-reduced-motion para accesibilidad y ausencia de una etiqueta semánticamente incorrecta.
.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;
}
El truco clave: display: inline-block en el contenido garantiza que el ancho del elemento sea igual al ancho del texto, y translateX(-100%) lo desplaza exactamente ese ancho hacia la izquierda. Si el contenido tiene display: block, translateX(-100%) será igual al ancho del contenedor, no al del texto, y la animación se romperá.
Para accesibilidad, agregue @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }: los usuarios con trastornos vestibulares verán texto estático con posibilidad de desplazamiento.
El marquee está justificado en escenarios donde el usuario no puede expandir el contenedor por sí mismo y el texto completo es críticamente importante: la pista actual en un reproductor en pantalla bloqueada, un ticker de noticias en un panel de TV o un marquee en una aplicación de transporte (estado del pedido).
El marquee no debe usarse en tablas, listas, formularios y diálogos, es decir, en cualquier lugar donde el usuario interactúe activamente con la interfaz. La animación distrae del objetivo, aumenta el tiempo de búsqueda de elementos y genera incomodidad. Según WebAIM (2024), 1 de cada 3 usuarios con trastornos vestibulares reporta molestias al ver texto animado.
Una regla general: si el texto se puede acortar o pasar a la siguiente línea, hágalo. El marquee es un último recurso, no una solución universal para etiquetas largas.
El marquee es una de las animaciones de UI más problemáticas desde el punto de vista de la accesibilidad. El texto en movimiento puede causar mareos, náuseas y desorientación en personas con trastornos vestibulares. Según WCAG 2.2, cualquier animación que dure más de 5 segundos debe tener un mecanismo de pausa o desactivación.
En iOS, use la propiedad UIAccessibility.isReduceMotionEnabled para verificar la configuración del sistema. Si reduceMotion está activada, no inicie la animación, muestre texto estático con ellipsize.
En Android, verifique la animación del sistema mediante Settings.Global.ANIMATOR_DURATION_SCALE. Si la animación global está desactivada (duration scale = 0), no se debe activar el marquee.
En la web, use la función multimedia prefers-reduced-motion: @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. Esta es una función CSS estándar compatible con todos los navegadores modernos. Compruébelo en DevTools: active la emulación de prefers-reduced-motion y verifique que la animación se desactive.
Preguntas frecuentes
Ellipsize trunca el texto y añade puntos suspensivos, ocultando parte del contenido. Marquee desplaza el texto completo, pero en movimiento. La elección depende de la prioridad: mostrar todo (marquee) o mantener la visualización estática (ellipsize).
El android:ellipsize="marquee" integrado no permite cambiar la velocidad, esta está determinada por el ScrollSpeed del sistema. Para una velocidad personalizada, use Animation o ValueAnimator manualmente.
Debido al reciclaje de vistas, setSelected(true) se restablece. Solución: cree un MarqueeTextView personalizado que sobrescriba isFocused() y devuelva true, o use animación personalizada en Adapter.onViewAttachedToWindow().
Sí, usando translateY en lugar de translateX. El marquee vertical se usa para créditos o tickers de noticias en feeds verticales. El principio es el mismo, pero cambia el eje de la animación.
En CSS: :hover { animation-play-state: paused; }. En Android: supervise el foco mediante OnFocusChangeListener y restablezca setSelected(false). En iOS: elimine la animación mediante layer.removeAnimation(forKey:).
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también