Le texte défilant (marquee) est un effet visuel dans lequel le texte défile automatiquement horizontalement dans une zone limitée, permettant d'afficher de longues chaînes dans un conteneur compact. Historiquement, le terme provient de la balise HTML <marquee>, populaire sur le web dans les années 1990. Dans le développement mobile moderne, le scrolling text est utilisé dans les lecteurs, les bandeaux d'informations et les barres d'état. Selon les Apple Human Interface Guidelines et Google Material Design, le marquee n'est acceptable que dans des scénarios limités où le tronquage statique avec des points de suspension dégrade l'UX.
Points clés
Le texte défilant est un effet d'interface utilisateur dans lequel le texte se déplace horizontalement dans une zone fixe, défilant cycliquement pour que l'utilisateur puisse lire tout le contenu qui ne tient pas dans le conteneur. Contrairement à ellipsize, qui cache une partie du texte, marquee affiche le texte entier, mais en mouvement.
Le terme marquee s'est imposé grâce à la balise HTML <marquee>, qui a été ajoutée dans les premières versions des navigateurs Netscape et Internet Explorer. La balise permettait de créer du texte défilant sans JavaScript ni CSS, mais n'a jamais fait partie du standard HTML. Les navigateurs modernes continuent de la prendre en charge pour la rétrocompatibilité, mais son utilisation n'est pas recommandée.
Dans le développement mobile, le marquee résout le problème des longues étiquettes dans les conteneurs compacts : noms de pistes dans un lecteur, tickers de devises, bandeaux d'informations sur les écrans TV. Cependant, en raison de l'animation, il crée une charge sur l'accessibilité et peut irriter les utilisateurs — il est donc utilisé de manière sélective.
Selon une étude du Nielsen Norman Group (2023), le texte en mouvement réduit la vitesse de lecture de 15 à 20 % et augmente la charge cognitive. N'utilisez le marquee que lorsque le tronquage statique (ellipsize) est vraiment inacceptable.
Sur Android, le marquee est implémenté par le mécanisme intégré de TextView via l'attribut android:ellipsize="marquee". C'est le seul mode ellipsize qui ne tronque pas le texte mais anime son défilement.
Pour activer le marquee, trois conditions doivent être remplies : singleLine="true" (ou maxLines="1"), ellipsize="marquee" et setSelected(true) dans le code. Sans appel à setSelected(), l'animation ne démarrera pas.
<!-- 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'attribut marqueeRepeatLimit détermine le nombre de répétitions de l'animation : marquee_forever (infini) ou un nombre spécifique. La valeur par défaut est marquee_forever. Si le texte est plus court que la largeur du conteneur, l'animation ne démarre pas — TextView affiche un texte statique.
Une caractéristique importante : setSelected(true) ne fonctionne que lorsque le TextView a le focus ou est en mode sélectionné. Dans RecyclerView, cela peut causer des problèmes — l'animation ne démarre que pour un seul élément. La solution est de créer un MarqueeTextView personnalisé qui remplace isFocused() et retourne toujours true.
À partir d'Android 9 (API 28), Google a amélioré le comportement du marquee — l'animation est devenue plus fluide et ne se fige pas lors du défilement d'une liste. Cependant, sur les appareils plus anciens, le marquee peut saccader, surtout à haute densité de pixels.
iOS n'a pas d'équivalent intégré à android:ellipsize="marquee". Le marquee est implémenté manuellement à l'aide de Core Animation ou d'une animation SwiftUI. L'approche la plus courante consiste à animer layer.position combiné avec un conteneur de clipping.
Dans UIKit, le marquee est construit avec trois éléments : un conteneur UILabel avec clipsToBounds = true, le texte lui-même et une CABasicAnimation pour décaler le texte sur l'axe X avec une répétition infinie et autoreverses = false, de sorte que la ligne défile dans une direction et revienne d'un bond.
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")
}
Calculer duration = textWidth / 60 donne une vitesse d'environ 60 pixels par seconde — un rythme de lecture confortable. Si la vitesse dépasse 120 px/s, le texte devient illisible. Si elle est inférieure à 30 px/s, l'animation semble lente. Ajustez la vitesse à votre scénario spécifique.
Dans SwiftUI, le marquee est implémenté plus simplement — via le modificateur offset() avec une animation répétable et .animation(.linear(duration:).repeatForever(autoreverses: false)). SwiftUI gère automatiquement le cycle de vie de l'animation mais nécessite GeometryReader pour déterminer la largeur du texte.
Sur le web, la balise obsolète <marquee> est déconseillée, bien que les navigateurs continuent de la prendre en charge. L'implémentation moderne du marquee est l'animation CSS avec des keyframes et transform: translateX.
Avantages de l'approche CSS : contrôle total sur la vitesse, la pause et la courbe d'animation, prise en charge de prefers-reduced-motion pour l'accessibilité et absence d'une balise sémantiquement incorrecte.
.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;
}
L'astuce clé : display: inline-block sur le contenu garantit que la largeur de l'élément est égale à la largeur du texte, et translateX(-100%) le décale exactement de cette largeur vers la gauche. Si le contenu est en display: block, translateX(-100%) sera égal à la largeur du conteneur, pas à celle du texte, et l'animation se brisera.
Pour l'accessibilité, ajoutez @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } } — les utilisateurs souffrant de troubles vestibulaires verront un texte statique avec possibilité de défilement.
Le marquee est justifié dans les scénarios où l'utilisateur ne peut pas agrandir le conteneur lui-même et où le texte complet est d'une importance cruciale : la piste en cours dans un lecteur sur écran verrouillé, un bandeau d'informations sur un panneau TV ou un marquee dans une application de covoiturage (statut de la commande).
Le marquee ne doit pas être utilisé dans les tableaux, listes, formulaires et dialogues — partout où l'utilisateur interagit activement avec l'interface. L'animation distrait de l'objectif, augmente le temps de recherche des éléments et crée un inconfort. Selon WebAIM (2024), 1 utilisateur sur 3 souffrant de troubles vestibulaires signale un inconfort lors de la visualisation de texte animé.
Une règle empirique : si le texte peut être raccourci ou passé à la ligne suivante — faites-le. Le marquee est un dernier recours, pas une solution universelle pour les longues étiquettes.
Le marquee est l'une des animations d'interface utilisateur les plus problématiques du point de vue de l'accessibilité. Le texte en mouvement peut provoquer des vertiges, des nausées et une désorientation chez les personnes souffrant de troubles vestibulaires. Selon WCAG 2.2, toute animation durant plus de 5 secondes doit disposer d'un mécanisme de pause ou de désactivation.
Sur iOS, utilisez la propriété UIAccessibility.isReduceMotionEnabled pour vérifier le réglage système. Si reduceMotion est activé — ne démarrez pas l'animation, affichez un texte statique avec ellipsize.
Sur Android, vérifiez l'animation système via Settings.Global.ANIMATOR_DURATION_SCALE. Si l'animation globale est désactivée (duration scale = 0), le marquee ne doit pas être activé.
Sur le web, utilisez la fonction média prefers-reduced-motion : @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. C'est une fonction CSS standard prise en charge par tous les navigateurs modernes. Vérifiez dans DevTools : activez l'émulation de prefers-reduced-motion et confirmez que l'animation se désactive.
Questions fréquentes
Ellipsize tronque le texte et ajoute des points de suspension, cachant une partie du contenu. Marquee fait défiler le texte entier, mais en mouvement. Le choix dépend de la priorité : tout afficher (marquee) ou maintenir un affichage statique (ellipsize).
Le android:ellipsize="marquee" intégré ne permet pas de modifier la vitesse — elle est déterminée par le ScrollSpeed système. Pour une vitesse personnalisée, utilisez Animation ou ValueAnimator manuellement.
En raison du recyclage des vues, setSelected(true) est réinitialisé. Solution : créez un MarqueeTextView personnalisé qui remplace isFocused() et retourne true, ou utilisez une animation personnalisée dans Adapter.onViewAttachedToWindow().
Oui, en utilisant translateY au lieu de translateX. Le marquee vertical est utilisé pour les crédits ou les bandeaux d'informations dans les flux verticaux. Le principe est le même, mais l'axe d'animation change.
En CSS : :hover { animation-play-state: paused; }. Sur Android : suivez le focus via OnFocusChangeListener et réinitialisez setSelected(false). Sur iOS : supprimez l'animation via layer.removeAnimation(forKey:).
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi