O texto rolante (marquee) é um efeito visual no qual o texto rola automaticamente horizontalmente dentro de uma área limitada, permitindo exibir strings longas em um contêiner compacto. Historicamente, o termo vem da tag HTML <marquee>, popular na web dos anos 1990. No desenvolvimento móvel moderno, o scrolling text é usado em players, tickers de notícias e barras de status. De acordo com as Apple Human Interface Guidelines e o Google Material Design, o marquee só é aceitável em cenários limitados onde o truncamento estático com reticências degrada a UX.
Pontos principais
O texto rolante é um efeito de UI no qual o texto se move horizontalmente dentro de uma área fixa, rolando ciclicamente para que o usuário possa ler todo o conteúdo que não cabe no contêiner. Ao contrário do ellipsize, que esconde parte do texto, o marquee mostra o texto inteiro, mas em movimento.
O termo marquee se consolidou graças à tag HTML <marquee>, que foi adicionada nas primeiras versões dos navegadores Netscape e Internet Explorer. A tag permitia criar texto rolante sem JavaScript ou CSS, mas nunca fez parte do padrão HTML. Os navegadores modernos continuam a suportá-la para compatibilidade retroativa, mas seu uso não é recomendado.
No desenvolvimento móvel, o marquee resolve o problema de rótulos longos em contêineres compactos: nomes de faixas em um player, tickers de moedas, notícias em telas de TV. No entanto, devido à animação, ele cria uma carga para a acessibilidade e pode irritar os usuários — portanto, é usado seletivamente.
De acordo com a pesquisa do Nielsen Norman Group (2023), o texto em movimento reduz a velocidade de leitura em 15-20% e aumenta a carga cognitiva. Use marquee apenas quando o truncamento estático (ellipsize) for realmente inaceitável.
No Android, o marquee é implementado pelo mecanismo integrado do TextView através do atributo android:ellipsize="marquee". Este é o único modo ellipsize que não trunca o texto, mas anima sua rolagem.
Para ativar o marquee, três condições devem ser atendidas: singleLine="true" (ou maxLines="1"), ellipsize="marquee" e setSelected(true) no código. Sem chamar setSelected(), a animação não será iniciada.
<!-- 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
O atributo marqueeRepeatLimit determina o número de repetições da animação: marquee_forever (infinito) ou um número específico. O padrão é marquee_forever. Se o texto for mais curto que a largura do contêiner, a animação não inicia — o TextView exibe texto estático.
Uma característica importante: setSelected(true) só funciona quando o TextView está com foco ou em modo selecionado. No RecyclerView, isso pode causar problemas — a animação só inicia para um item. A solução é usar um MarqueeTextView personalizado que sobrescreva isFocused() e sempre retorne true.
A partir do Android 9 (API 28), o Google melhorou o comportamento do marquee — a animação ficou mais suave e não congela ao rolar uma lista. No entanto, em dispositivos mais antigos, o marquee pode tremer, especialmente em altas densidades de pixels.
O iOS não tem um equivalente integrado ao android:ellipsize="marquee". O marquee é implementado manualmente usando Core Animation ou animação SwiftUI. A abordagem mais comum é animar layer.position combinado com um contêiner de recorte.
No UIKit, o marquee é construído com três elementos: um contêiner UILabel com clipsToBounds = true, o próprio texto e uma CABasicAnimation para deslocar o texto no eixo X com repetição infinita e autoreverses = false, para que a linha role em uma direção e volte com um 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 dá uma velocidade de aproximadamente 60 pixels por segundo — um ritmo confortável para leitura. Se a velocidade exceder 120 px/s, o texto se torna ilegível. Se for inferior a 30 px/s, a animação parece lenta. Ajuste a velocidade para seu cenário específico.
No SwiftUI, o marquee é implementado de forma mais simples — usando o modificador offset() com animação repetível e .animation(.linear(duration:).repeatForever(autoreverses: false)). O SwiftUI gerencia automaticamente o ciclo de vida da animação, mas requer GeometryReader para determinar a largura do texto.
Na web, a tag obsoleta <marquee> não é recomendada para uso, embora os navegadores continuem a suportá-la. A implementação moderna do marquee é a animação CSS com keyframes e transform: translateX.
Vantagens da abordagem CSS: controle total sobre velocidade, pausa e curva de animação, suporte a prefers-reduced-motion para acessibilidade e ausência de uma tag semanticamente incorreta.
.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;
}
O truque principal: display: inline-block no conteúdo garante que a largura do elemento seja igual à largura do texto, e translateX(-100%) o desloca exatamente essa largura para a esquerda. Se o conteúdo for display: block, translateX(-100%) será igual à largura do contêiner, não à do texto, e a animação quebrará.
Para acessibilidade, adicione @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } } — usuários com distúrbios vestibulares verão texto estático com capacidade de rolagem.
O marquee é justificado em cenários onde o usuário não pode expandir o contêiner por si mesmo e o texto completo é criticamente importante: a faixa atual em um player na tela bloqueada, um ticker de notícias em um painel de TV ou um marquee em um aplicativo de transporte (status do pedido).
O marquee não deve ser usado em tabelas, listas, formulários e diálogos — em qualquer lugar onde o usuário interaja ativamente com a interface. A animação distrai do objetivo, aumenta o tempo de busca de elementos e causa desconforto. De acordo com a WebAIM (2024), 1 em cada 3 usuários com distúrbios vestibulares relata desconforto ao visualizar texto animado.
Uma regra prática: se o texto pode ser encurtado ou quebrado para a próxima linha — faça isso. O marquee é um último recurso, não uma solução universal para rótulos longos.
O marquee é uma das animações de UI mais problemáticas do ponto de vista da acessibilidade. O texto em movimento pode causar tontura, náusea e desorientação em pessoas com distúrbios vestibulares. De acordo com a WCAG 2.2, qualquer animação que dure mais de 5 segundos deve ter um mecanismo de pausa ou desativação.
No iOS, use a propriedade UIAccessibility.isReduceMotionEnabled para verificar a configuração do sistema. Se reduceMotion estiver ativado — não inicie a animação, mostre texto estático com ellipsize.
No Android, verifique a animação do sistema através de Settings.Global.ANIMATOR_DURATION_SCALE. Se a animação global estiver desativada (duration scale = 0), o marquee não deve ser ativado.
Na web, use o recurso de mídia prefers-reduced-motion: @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. Este é um recurso CSS padrão suportado por todos os navegadores modernos. Verifique no DevTools: ative a emulação de prefers-reduced-motion e confirme que a animação desliga.
Perguntas frequentes
Ellipsize trunca o texto e adiciona reticências, escondendo parte do conteúdo. Marquee rola o texto inteiro, mas em movimento. A escolha depende da prioridade: mostrar tudo (marquee) ou manter a exibição estática (ellipsize).
O android:ellipsize="marquee" integrado não permite alterar a velocidade — ela é determinada pelo ScrollSpeed do sistema. Para velocidade personalizada, use Animation ou ValueAnimator manualmente.
Devido à reciclagem de views, setSelected(true) é redefinido. Solução: crie um MarqueeTextView personalizado que sobrescreva isFocused() e retorne true, ou use animação personalizada em Adapter.onViewAttachedToWindow().
Sim, usando translateY em vez de translateX. O marquee vertical é usado para créditos ou tickers de notícias em feeds verticais. O princípio é o mesmo, mas o eixo da animação muda.
Em CSS: :hover { animation-play-state: paused; }. No Android: monitore o foco via OnFocusChangeListener e redefina setSelected(false). No iOS: remova a animação via layer.removeAnimation(forKey:).
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também