النص المتحرك في التطبيقات — ما هو، كيفية إنشائه وتكوينه

المؤلف: IT Sectr نُشر: 2026-07-25 وقت القراءة: 9 دق

النص المتحرك (marquee) هو تأثير بصري يتم فيه تمرير النص تلقائياً أفقياً داخل منطقة محددة، مما يسمح بعرض الأسطر الطويلة في حاوية مدمجة. تاريخياً، يأتي المصطلح من وسم HTML <marquee>، الذي كان شائعاً على الويب في التسعينيات. في تطوير التطبيقات الحديثة، يُستخدم scrolling text في المشغلات، وشريط الأخبار، وأشرطة الحالة. وفقاً لإرشادات Apple Human Interface و Google Material Design، فإن النص المتحرك مقبول فقط في السيناريوهات المحدودة حيث يؤدي القطع الثابت بعلامات الحذف إلى تدهور تجربة المستخدم.

الخلاصة

  • النص المتحرك — تمرير أفقي تلقائي للنص الطويل في حاوية محدودة.
  • على Android يتم تنفيذه عبر android:ellipsize="marquee" في TextView.
  • على iOS يتم التأثير يدوياً عبر Core Animation أو SwiftUI مع إزاحة متحركة.
  • على الويب، وسم <marquee> قديم؛ يُستخدم تحريك CSS مع translateX.
  • لإمكانية الوصول، دعم prefers-reduced-motion إلزامي.

ما هو النص المتحرك؟

النص المتحرك هو تأثير واجهة مستخدم يتحرك فيه النص أفقياً داخل منطقة ثابتة، ويتحرك دورياً ليتمكن المستخدم من قراءة كل المحتوى الذي لا يتسع في الحاوية. على عكس ellipsize الذي يخفي جزءاً من النص، يعرض marquee النص بالكامل ولكن متحركاً.

المصطلح marquee ترسخ بفضل وسم HTML <marquee> الذي أُضيف في الإصدارات المبكرة من متصفحي Netscape و Internet Explorer. كان الوسم يسمح بإنشاء نص متحرك بدون JavaScript أو CSS، لكنه لم يكن جزءاً من معيار HTML قط. تستمر المتصفحات الحديثة في دعمه للتوافق مع الإصدارات القديمة، لكن لا يُنصح باستخدامه.

في تطوير التطبيقات، يحل marquee مشكلة التسميات الطويلة في الحاويات المدمجة: أسماء المسارات في المشغل، وأسعار العملات، والأخبار على شاشات التلفزيون. ولكن بسبب التحريك، يُشكل عبئاً على إمكانية الوصول وقد يزعج المستخدمين — لذلك يُستخدم بشكل انتقائي.

وفقاً لبحث Nielsen Norman Group (2023)، فإن النص المتحرك يقلل سرعة القراءة بنسبة 15-20% ويزيد العبء المعرفي. استخدم marquee فقط عندما يكون القطع الثابت (ellipsize) غير مقبول حقاً.

النص المتحرك على Android عبر TextView

على Android، يُنفذ marquee عبر الآلية المدمجة في TextView باستخدام السمة android:ellipsize="marquee". هذا هو وضع ellipsize الوحيد الذي لا يقطع النص بل يحرك تمريره.

لتفعيل marquee يجب توفر ثلاثة شروط: singleLine="true" (أو maxLines="1")، و ellipsize="marquee"، و setSelected(true) في الكود. بدون استدعاء setSelected()، لن تبدأ الحركة.

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

تحدد السمة marqueeRepeatLimit عدد مرات تكرار الحركة: marquee_forever (لا نهائي) أو رقم محدد. القيمة الافتراضية هي marquee_forever. إذا كان النص أقصر من عرض الحاوية، لا تبدأ الحركة — يعرض TextView نصاً ثابتاً.

ميزة مهمة: setSelected(true) تعمل فقط عندما يكون TextView في حالة التركيز أو في الوضع المحدد. في RecyclerView، قد يسبب هذا مشاكل — تبدأ الحركة لعنصر واحد فقط. الحل: استخدام MarqueeTextView مخصص يجاوز isFocused() ويعيد true دائماً.

ابتداءً من Android 9 (API 28)، حسنت Google سلوك marquee — أصبحت الحركة أكثر سلاسة ولا تتجمد عند تمرير القائمة. لكن على الأجهزة القديمة، قد يتعطل marquee خاصة في كثافات البكسل العالية.

تنفيذ النص المتحرك على iOS

iOS لا يحتوي على مكافئ مدمج لـ android:ellipsize="marquee". يُنفذ marquee يدوياً عبر Core Animation أو تحريك SwiftUI. النهج الأكثر شيوعاً هو تحريك layer.position مع حاوية مقطوعة.

في UIKit، يُبنى marquee من ثلاثة عناصر: حاوية UILabel مع clipsToBounds = true، والنص نفسه، و CABasicAnimation لتحريك النص على المحور X بتكرار لا نهائي و autoreverses = false، لينزلق السطر في اتجاه واحد ويعود قفزة.

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

حساب duration = textWidth / 60 يعطي سرعة حوالي 60 بكسل في الثانية — وهي وتيرة قراءة مريحة. إذا تجاوزت السرعة 120 بكسل/ثانية، يصبح النص غير قابل للقراءة. إذا كانت أقل من 30 بكسل/ثانية، تبدو الحركة بطيئة. اضبط السرعة حسب السيناريو الخاص بك.

في SwiftUI، يُنفذ marquee ببساطة أكبر — عبر معدل offset() مع حركة قابلة للتكرار و .animation(.linear(duration:).repeatForever(autoreverses: false)). يدير SwiftUI دورة حياة الحركة تلقائياً لكنه يتطلب GeometryReader لتحديد عرض النص.

تحريك CSS بدلاً من وسم marquee

على الويب، وسم <marquee> القديم غير موصى به للاستخدام، رغم أن المتصفحات تستمر في دعمه. التنفيذ الحديث للنص المتحرك هو تحريك CSS مع keyframes و transform: translateX.

مزايا نهج CSS: تحكم كامل في السرعة والإيقاف ومنحنى الحركة، ودعم prefers-reduced-motion لإمكانية الوصول، وغياب وسم غير صحيح دلالياً.

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

الحيلة الأساسية: display: inline-block في المحتوى يضمن أن عرض العنصر يساوي عرض النص، و translateX(-100%) يزيحه بمقدار هذا العرض إلى اليسار. إذا كان المحتوى display: block، فإن translateX(-100%) سيساوي عرض الحاوية وليس عرض النص، وستنكسر الحركة.

لإمكانية الوصول، أضف @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } } — المستخدمون الذين يعانون من اضطرابات دهليزية سيرون نصاً ثابتاً مع إمكانية التمرير.

متى تستخدم ومتى تتجنب

النص المتحرك مبرر في السيناريوهات التي لا يستطيع المستخدم فيها توسيع الحاوية بنفسه والنص الكامل مهم بشكل حاسم: المسار الحالي في مشغل على شاشة مقفلة، وشريط أخبار على لوحة تلفزيون، ونص متحرك في تطبيق نقل (حالة الطلب).

لا يُستخدم النص المتحرك في الجداول والقوائم والنماذج والحوارات — حيث يتفاعل المستخدم بنشاط مع الواجهة. الحركة تشتت الانتباه عن الهدف، وتزيد وقت البحث عن العنصر، وتخلق عدم ارتياح. وفقاً لـ WebAIM (2024)، 1 من كل 3 مستخدمين يعانون من اضطرابات دهليزية يبلغون عن عدم الراحة عند مشاهدة نص متحرك.

قاعدة عامة: إذا كان يمكن تقصير النص أو وضعه في السطر التالي — فافعل ذلك. النص المتحرك هو الحل الأخير، وليس حلاً عالمياً للتسميات الطويلة.

إمكانية الوصول: التحريك والحركة المخفضة

النص المتحرك هو أحد أكثر حركات واجهة المستخدم إشكالية من وجهة نظر إمكانية الوصول. يمكن أن يسبب النص المتحرك دواراً وغثياناً وارتباكاً للأشخاص الذين يعانون من اضطرابات دهليزية. وفقاً لـ WCAG 2.2، يجب أن يحتوي أي تحريك يستمر لأكثر من 5 ثوانٍ على آلية إيقاف أو تعطيل.

على iOS، استخدم الخاصية UIAccessibility.isReduceMotionEnabled للتحقق من إعداد النظام. إذا كان reduceMotion مفعّلاً — لا تبدأ الحركة، اعرض نصاً ثابتاً مع ellipsize.

على Android، تحقق من حركة النظام عبر Settings.Global.ANIMATOR_DURATION_SCALE. إذا كانت الحركة العامة معطلة (duration scale = 0)، فلا يجب تفعيل marquee.

على الويب، استخدم ميزة الوسائط prefers-reduced-motion: @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. هذه ميزة CSS قياسية مدعومة من جميع المتصفحات الحديثة. تحقق في DevTools: فعّل محاكاة prefers-reduced-motion وتأكد من إيقاف الحركة.

الأسئلة الشائعة

ما الفرق بين Marquee و Ellipsize؟

Ellipsize يقطع النص ويضيف علامات حذف، مخفياً جزءاً من المحتوى. Marquee يمرر النص بالكامل ولكن متحركاً. يعتمد الاختيار على الأولوية: إظهار كل شيء (marquee) أو الحفاظ على العرض الثابت (ellipsize).

كيف تسريع أو إبطاء النص المتحرك على Android؟

android:ellipsize="marquee" المدمج لا يسمح بتغيير السرعة — يتم تحديدها بواسطة ScrollSpeed النظام. للسرعة المخصصة، استخدم Animation أو ValueAnimator يدوياً.

لماذا لا يعمل marquee في RecyclerView؟

بسبب إعادة استخدام العناصر، يتم إعادة تعيين setSelected(true). الحل: أنشئ MarqueeTextView مخصصاً يجاوز isFocused() ويعيد true، أو استخدم حركة مخصصة في Adapter.onViewAttachedToWindow().

هل يمكن عمل marquee عمودي؟

نعم، باستخدام translateY بدلاً من translateX. يُستخدم marquee العمودي للاعتمادات أو شريط الأخبار في التغذيات الرأسية. المبدأ هو نفسه لكن محور الحركة يتغير.

كيف إيقاف النص المتحرك عند التركيز؟

في CSS: :hover { animation-play-state: paused; }. على Android: تتبع التركيز عبر OnFocusChangeListener وأعد تعيين setSelected(false). على iOS: أزل الحركة عبر layer.removeAnimation(forKey:).

الملخص

  • النص المتحرك — تمرير أفقي للنص يعرض المحتوى بالكامل من خلال الحركة.
  • على Android، android:ellipsize="marquee" المدمج يتطلب setSelected(true) و singleLine="true".
  • على iOS، يتم التنفيذ عبر CABasicAnimation باستخدام position.x.
  • على الويب، وسم <marquee> قديم؛ استخدم CSS @keyframes مع translateX.
  • استخدم marquee فقط عندما يكون ellipsize غير مقبول — في المشغلات، الأشرطة، أشرطة الحالة.
  • أوقف الحركة بالتأكيد عند prefers-reduced-motion على جميع المنصات.
  • سرعة الحركة: ~60 بكسل/ثانية — مريحة للقراءة، فوق 120 بكسل/ثانية — غير قابلة للقراءة.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا