Chữ chạy trong ứng dụng — nó là gì, cách tạo và cấu hình

Tác giả: IT Sectr Đã đăng: 2026-07-25 Thời gian đọc: 9 phút

Chữ chạy (marquee) là hiệu ứng trực quan trong đó văn bản tự động cuộn theo chiều ngang trong một khu vực giới hạn, cho phép hiển thị các chuỗi dài trong một vùng chứa nhỏ gọn. Về mặt lịch sử, thuật ngữ này bắt nguồn từ thẻ HTML <marquee>, phổ biến trên web vào những năm 1990. Trong phát triển di động hiện đại, scrolling text được sử dụng trong trình phát, băng tin và thanh trạng thái. Theo Apple Human Interface Guidelines và Google Material Design, marquee chỉ được chấp nhận trong các kịch bản hạn chế khi việc cắt tĩnh bằng dấu ba chấm làm giảm trải nghiệm người dùng.

Các điểm chính

  • Chữ chạy — cuộn ngang tự động của văn bản dài trong vùng chứa giới hạn.
  • Trên Android, nó được triển khai qua android:ellipsize="marquee" trong TextView.
  • Trên iOS, hiệu ứng được thực hiện thủ công qua Core Animation hoặc SwiftUI với hiệu ứng offset.
  • Trên web, thẻ <marquee> đã lỗi thời; thay vào đó dùng CSS animation với translateX.
  • Để hỗ trợ tiếp cận, bắt buộc phải hỗ trợ prefers-reduced-motion.

Chữ chạy là gì?

Chữ chạy là hiệu ứng giao diện người dùng trong đó văn bản di chuyển theo chiều ngang trong một khu vực cố định, cuộn theo chu kỳ để người dùng có thể đọc toàn bộ nội dung không vừa trong vùng chứa. Không giống như ellipsize, ẩn một phần văn bản, marquee hiển thị toàn bộ văn bản nhưng đang chuyển động.

Thuật ngữ marquee được thiết lập nhờ thẻ HTML <marquee>, được thêm vào trong các phiên bản đầu của trình duyệt Netscape và Internet Explorer. Thẻ này cho phép tạo văn bản cuộn mà không cần JavaScript hay CSS, nhưng chưa bao giờ là một phần của tiêu chuẩn HTML. Các trình duyệt hiện đại tiếp tục hỗ trợ nó để tương thích ngược, nhưng không khuyến khích sử dụng.

Trong phát triển di động, marquee giải quyết vấn đề nhãn dài trong vùng chứa nhỏ gọn: tên bản nhạc trong trình phát, bảng giá tiền tệ, băng tin trên màn hình TV. Tuy nhiên, do hiệu ứng, nó tạo ra gánh nặng cho khả năng tiếp cận và có thể gây khó chịu cho người dùng — vì vậy nó được sử dụng có chọn lọc.

Theo nghiên cứu của Nielsen Norman Group (2023), văn bản chuyển động làm giảm tốc độ đọc 15-20% và tăng tải nhận thức. Chỉ sử dụng marquee khi việc cắt tĩnh (ellipsize) thực sự không thể chấp nhận được.

Chữ chạy trên Android qua TextView

Trên Android, marquee được triển khai thông qua cơ chế tích hợp của TextView qua thuộc tính android:ellipsize="marquee". Đây là chế độ ellipsize duy nhất không cắt văn bản mà tạo hiệu ứng cuộn.

Để kích hoạt marquee, cần đáp ứng ba điều kiện: singleLine="true" (hoặc maxLines="1"), ellipsize="marquee" và setSelected(true) trong mã. Nếu không gọi setSelected(), hiệu ứng sẽ không bắt đầu.

xml
<!-- 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

Thuộc tính marqueeRepeatLimit xác định số lần lặp lại hiệu ứng: marquee_forever (vô hạn) hoặc một số cụ thể. Giá trị mặc định là marquee_forever. Nếu văn bản ngắn hơn chiều rộng vùng chứa, hiệu ứng sẽ không bắt đầu — TextView hiển thị văn bản tĩnh.

Một đặc điểm quan trọng: setSelected(true) chỉ hoạt động khi TextView có tiêu điểm hoặc ở chế độ được chọn. Trong RecyclerView, điều này có thể gây ra sự cố — hiệu ứng chỉ bắt đầu cho một mục. Giải pháp: tạo MarqueeTextView tùy chỉnh ghi đè isFocused() và luôn trả về true.

Bắt đầu từ Android 9 (API 28), Google đã cải thiện hành vi của marquee — hiệu ứng trở nên mượt mà hơn và không bị đóng băng khi cuộn danh sách. Tuy nhiên, trên các thiết bị cũ, marquee có thể bị giật, đặc biệt ở mật độ điểm ảnh cao.

Triển khai chữ chạy trên iOS

iOS không có tương đương tích hợp cho android:ellipsize="marquee". Marquee được triển khai thủ công bằng Core Animation hoặc hiệu ứng SwiftUI. Cách tiếp cận phổ biến nhất là tạo hiệu ứng cho layer.position kết hợp với vùng chứa cắt.

Trong UIKit, marquee được xây dựng từ ba phần tử: vùng chứa UILabel với clipsToBounds = true, văn bản và CABasicAnimation để dịch chuyển văn bản theo trục X với lặp vô hạn và autoreverses = false, để dòng cuộn theo một hướng và nhảy trở lại.

swift
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")
}

Tính toán duration = textWidth / 60 cho tốc độ khoảng 60 pixel mỗi giây — tốc độ đọc thoải mái. Nếu tốc độ vượt quá 120 px/s, văn bản trở nên không đọc được. Nếu dưới 30 px/s, hiệu ứng trông chậm chạp. Điều chỉnh tốc độ cho kịch bản cụ thể của bạn.

Trong SwiftUI, marquee được triển khai đơn giản hơn — qua bộ điều chỉnh offset() với hiệu ứng lặp và .animation(.linear(duration:).repeatForever(autoreverses: false)). SwiftUI tự động quản lý vòng đời hiệu ứng nhưng yêu cầu GeometryReader để xác định chiều rộng văn bản.

CSS animation thay cho thẻ marquee

Trên web, thẻ <marquee> lỗi thời không được khuyến khích sử dụng, mặc dù trình duyệt vẫn hỗ trợ nó. Triển khai hiện đại của marquee là CSS animation với keyframes và transform: translateX.

Ưu điểm của cách tiếp cận CSS: kiểm soát hoàn toàn tốc độ, tạm dừng và đường cong hiệu ứng, hỗ trợ prefers-reduced-motion cho khả năng tiếp cận và không có thẻ không đúng ngữ nghĩa.

css
.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;
}

Mẹo chính: display: inline-block trên nội dung đảm bảo chiều rộng của phần tử bằng chiều rộng văn bản và translateX(-100%) dịch chuyển nó chính xác bằng chiều rộng đó sang trái. Nếu nội dung là display: block, translateX(-100%) sẽ bằng chiều rộng vùng chứa, không phải chiều rộng văn bản, và hiệu ứng sẽ hỏng.

Để hỗ trợ tiếp cận, thêm @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } } — người dùng bị rối loạn tiền đình sẽ thấy văn bản tĩnh với khả năng cuộn.

Khi nào sử dụng và khi nào tránh

Marquee được biện minh trong các kịch bản mà người dùng không thể tự mở rộng vùng chứa và toàn bộ văn bản rất quan trọng: bản nhạc hiện tại trong trình phát trên màn hình khóa, băng tin trên bảng TV hoặc marquee trong ứng dụng gọi xe (trạng thái đơn hàng).

Marquee không nên được sử dụng trong bảng, danh sách, biểu mẫu và hộp thoại — bất cứ nơi nào người dùng tương tác tích cực với giao diện. Hiệu ứng làm xao nhãng khỏi mục tiêu, tăng thời gian tìm kiếm phần tử và tạo ra sự khó chịu. Theo WebAIM (2024), 1 trong 3 người dùng bị rối loạn tiền đình báo cáo sự khó chịu khi xem văn bản hoạt hình.

Nguyên tắc chung: nếu văn bản có thể được rút ngắn hoặc xuống dòng tiếp theo — hãy làm điều đó. Marquee là giải pháp cuối cùng, không phải giải pháp phổ quát cho nhãn dài.

Khả năng tiếp cận: hiệu ứng và giảm chuyển động

Marquee là một trong những hiệu ứng UI có vấn đề nhất từ góc độ khả năng tiếp cận. Văn bản chuyển động có thể gây chóng mặt, buồn nôn và mất phương hướng ở những người bị rối loạn tiền đình. Theo WCAG 2.2, bất kỳ hiệu ứng nào kéo dài hơn 5 giây phải có cơ chế tạm dừng hoặc tắt.

Trên iOS, sử dụng thuộc tính UIAccessibility.isReduceMotionEnabled để kiểm tra cài đặt hệ thống. Nếu reduceMotion được bật — không bắt đầu hiệu ứng, hiển thị văn bản tĩnh với ellipsize.

Trên Android, kiểm tra hiệu ứng hệ thống qua Settings.Global.ANIMATOR_DURATION_SCALE. Nếu hiệu ứng toàn cục bị tắt (duration scale = 0), không nên bật marquee.

Trên web, sử dụng tính năng đa phương tiện prefers-reduced-motion: @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. Đây là tính năng CSS tiêu chuẩn được hỗ trợ bởi tất cả các trình duyệt hiện đại. Kiểm tra trong DevTools: bật mô phỏng prefers-reduced-motion và xác nhận rằng hiệu ứng tắt.

Câu hỏi thường gặp

Marquee khác Ellipsize như thế nào?

Ellipsize cắt văn bản và thêm dấu ba chấm, ẩn một phần nội dung. Marquee cuộn toàn bộ văn bản nhưng đang chuyển động. Lựa chọn phụ thuộc vào ưu tiên: hiển thị mọi thứ (marquee) hoặc duy trì hiển thị tĩnh (ellipsize).

Làm thế nào để tăng hoặc giảm tốc độ marquee trên Android?

android:ellipsize="marquee" tích hợp không cho phép thay đổi tốc độ — nó được xác định bởi ScrollSpeed hệ thống. Để có tốc độ tùy chỉnh, hãy sử dụng Animation hoặc ValueAnimator thủ công.

Tại sao marquee không hoạt động trong RecyclerView?

Do tái sử dụng view, setSelected(true) bị đặt lại. Giải pháp: tạo MarqueeTextView tùy chỉnh ghi đè isFocused() và trả về true, hoặc sử dụng hiệu ứng tùy chỉnh trong Adapter.onViewAttachedToWindow().

Có thể tạo marquee dọc không?

Có, bằng cách sử dụng translateY thay vì translateX. Marquee dọc được sử dụng cho credits hoặc băng tin trong luồng dọc. Nguyên tắc giống nhau, nhưng trục hiệu ứng thay đổi.

Làm thế nào để dừng marquee khi có tiêu điểm?

Trong CSS: :hover { animation-play-state: paused; }. Trên Android: theo dõi tiêu điểm qua OnFocusChangeListener và đặt lại setSelected(false). Trên iOS: xóa hiệu ứng qua layer.removeAnimation(forKey:).

Tổng kết

  • Chữ chạy — cuộn ngang văn bản, hiển thị toàn bộ nội dung qua hiệu ứng.
  • Trên Android, android:ellipsize="marquee" tích hợp yêu cầu setSelected(true) và singleLine="true".
  • Trên iOS, triển khai qua CABasicAnimation với đường dẫn khóa position.x.
  • Trên web, thẻ <marquee> đã lỗi thời; sử dụng CSS @keyframes với translateX.
  • Sử dụng marquee chỉ khi ellipsize không thể chấp nhận — trong trình phát, băng tin, thanh trạng thái.
  • Nhớ tắt hiệu ứng cho prefers-reduced-motion trên tất cả nền tảng.
  • Tốc độ hiệu ứng: ~60 px/s — thoải mái để đọc, trên 120 px/s — không đọc được.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm