إنشاء مؤشر تقدّم التمرير

مؤشر تقدّم التمرير هو نمط شائع لواجهة المستخدم يعرض بشكل مرئي تقدّم المستخدم في مستند أو حاوية قابلة للتمرير. أثناء تنقّل المستخدم في الصفحة، يتم تعديل عنصر مرئي ليعكس موضعه، ما يمنحه فكرة واضحة وبديهية عن مقدار المحتوى الذي تم الاطّلاع عليه والمقدار المتبقي.

كيفية الإعداد

لإنشاء مؤشر تقدّم التمرير، تحتاج إلى أمرين:

  1. عنصر يعمل كشريط تقدّم يكون هذا العنصر عادةً position: fixed أو position: absolute ليبقى ظاهرًا أثناء انتقال المستخدم إلى أماكن أخرى.
  2. صورة متحرّكة مرتبطة بموضع الانتقال

إليك كيفية تحقيق ذلك:

  • أولاً، أنشئ عنصر HTML ليكون بمثابة شريط التقدم. يمكن تصميم هذا العنصر حسب رغبتك.
  • بعد ذلك، في CSS، حدِّد صورة متحركة @keyframes لتغيير حجم شريط التقدم. من الطرق الشائعة تغيير حجم العنصر من scaleX(0) إلى scaleX(1).
  • أخيرًا، طبِّق هذه الحركة على عنصر شريط التقدّم واضبط animation-timeline على مخطط زمني للتمرير. يؤدي ذلك إلى توجيه المتصفّح إلى تقدّم الحركة استنادًا إلى موضع الانتقال في أقرب أداة انتقال للأعلى أو للأسفل.

مثال على الرمز

يعمل هذا الرمز على تكبير العنصر #progress عند الانتقال للأسفل باستخدام مخطط زمني مجهول للتمرير، تم إنشاؤه بواسطة الدالة scroll().

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: scroll())) {
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    #progress {
      position: fixed;
      left: 0; top: 0;
      width: 100%; height: 1em;
      background: red;

      transform-origin: 0 50%;
      animation: grow-progress auto linear;
      animation-timeline: scroll();
    }
  }
}

بسبب موقعها في نموذج العناصر في المستند (DOM)، ستتتبّع الدالة scroll() أقرب شريط تمرير رئيسي في الاتجاه block، وهو في هذه الحالة شريط التمرير الجذر.

<body>
  <!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
  <div id="progress" aria-hidden="true"></div>
</body>

يعمل هذا الرمز على تكبير العنصر #progress عند الانتقال للأسفل باستخدام مخطط زمني مسمّى للتمرير، تم إنشاؤه بواسطة السمة scroll-timeline.

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: scroll())) {
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    :root {
      scroll-timeline: --tl block;
    }

    #progress {
      position: fixed;
      left: 0; top: 0;
      width: 100%; height: 1em;
      background: red;

      transform-origin: 0 50%;
      animation: grow-progress auto linear;
      animation-timeline: --tl;
    }
  }
}

أفضل الممارسات

عند استخدام الصور المتحركة المستندة إلى الانتقال، من المهم اتّباع بعض أفضل الممارسات لضمان تجربة سلسة وسهلة الاستخدام:

  • يجب تضمين ميزة رصد الميزات: لا تتيح بعض المتصفّحات استخدام الصور المتحركة المستندة إلى الانتقال للأعلى أو للأسفل. استخدِم @supports (animation-timeline: scroll()) للتحقّق من توفّر الدعم وتوفير خيار احتياطي للمتصفّحات التي لا تتيح هذه الميزة.
    • لا تستخدِم حزمة scroll-timeline-polyfill لاستراتيجية الاحتياط لأنّها لا تتضمّن جميع الميزات وتحتوي على الكثير من المشاكل المعروفة.
    • إذا كانت الحركة تُعتبر زخرفية فقط، فاختَر "التحسين التدريجي" ولا تقدّم بديلاً.
  • يجب إزالة العناصر الزخرفية البحتة من مسارات قراءة التكنولوجيا المساعِدة: طبِّق السمة aria-hidden="true" على مؤشرات الانتقال المرئية البحتة لضمان عدم مواجهة برامج قراءة الشاشة لعُقد فارغة وغير مُسمّاة.
  • يجب احترام الإعدادات المفضّلة للمستخدمين: يفضّل بعض المستخدمين تقليل الحركة على الويب. استخدِم الاستعلام عن الوسائط prefers-reduced-motion لإيقاف الصور المتحركة أو تقليلها لهؤلاء المستخدمين.
  • يُرجى محاولة تحريك خصائص CSS ذات الأداء العالي فقط: للحصول على صور متحركة أكثر سلاسة، التزِم بتحريك الخصائص التي يمكن معالجتها من خلال سلسلة عمليات التجميع في المتصفّح، مثل transform وopacity. قد تؤدي إضافة تأثيرات حركية إلى سمات أخرى، مثل width أو height، إلى حدوث مشاكل في الأداء.
  • يجب استخدام ترتيب التعريف الصحيح: عند استخدام السمة المختصرة animation، يجب تعريف animation-timeline بعدها لمنع السمة المختصرة من إعادة ضبط المخطط الزمني.

عند استخدام الدالة scroll() لإنشاء تأثير حركي مستند إلى الانتقال:

  • اختياري: حدِّد شريط التمرير بوضوح: عندما لا تستهدف أقرب شريط تمرير رئيسي، حدِّد بوضوح شريط التمرير الذي تريد استخدامه مع scroll(root) أو scroll(self).
    • عندما لا تكون السمة root أو nearest أو self كافية، استخدِم مخططًا زمنيًا للتمرير يحمل اسمًا.
  • اختياري: حدِّد المحور الذي تريد تتبُّعه: عندما لا تستهدف المحور التلقائي block (كما هو الحال في شريط التمرير الأفقي)، حدِّد المحور الذي تريد تتبُّعه باستخدام scroll(block) أو scroll(inline).

عند استخدام السمة scroll-timeline لإنشاء صورة متحركة مستندة إلى الانتقال للأعلى أو للأسفل:

  • يجب استخدام <dashed-ident> لخدمة CSS في الاسم.
  • اختياري تحديد المحور الذي تريد تتبُّعه: في حال عدم استهداف محور block التلقائي (مثلاً في شريط تمرير أفقي)، حدِّد المحور الذي تريد تتبُّعه باستخدام scroll-timeline-axis.
  • يجب التأكّد من أنّ نطاق البحث يعمل: عندما لا يكون العنصر الذي يعرّف scroll-timeline عنصرًا رئيسيًا في الشجرة المسطّحة للعنصر المتحرّك، يمكنك رفع مستوى ظهور اسم scroll-timeline باستخدام timeline-scope على عنصر رئيسي مشترك.

استراتيجيات احتياطية

بالنسبة إلى المتصفّحات التي لا تتيح استخدام الصور المتحركة المستندة إلى الانتقال للأعلى أو للأسفل، يمكنك استخدام خيار احتياطي لإعادة إنشاء التأثيرات المرئية. يتم عادةً إنشاء الإجراءات الاحتياطية باستخدام أداة استماع للتمرير (لتأثيرات ScrollTimeline) أو واجهة برمجة التطبيقات IntersectionObserver (لتأثيرات ViewTimeline).

في المتصفّحات التي تتضمّن دعمًا مدمجًا للصور المتحركة المستندة إلى الانتقال، استخدِم دائمًا تنفيذ CSS الأصلي لأنّها أكثر فعالية.

يُرجى العِلم أنّه لا يمكن إعادة إنشاء كل تأثير باستخدام نهج الإجراءات الاحتياطية.

بالنسبة إلى حالة الاستخدام هذه تحديدًا، يطبّق النص البرمجي التالي الإجراء الاحتياطي للمتصفّحات التي لا تتيح استخدام الصور المتحركة المستندة إلى الانتقال للأعلى أو للأسفل. يستخدم هذا الرمز مستمعًا للتمرير لتتبُّع موضع التمرير للعنصر الجذر ويعدّل السمة transform لشريط التقدّم وفقًا لذلك.

<script>
  if (!CSS.supports('animation-timeline', 'scroll()')) {
    const progress = document.querySelector('#progress');

    window.addEventListener('scroll', () => {
      const scrollable = document.documentElement.scrollHeight - window.innerHeight;
      const scrolled = window.scrollY;
      const progressPercentage = (scrolled / scrollable);

      progress.style.transform = `scaleX(${progressPercentage})`;
    });
  }
</script>