إنشاء مكوِّن شريط التحميل

تقدّم هذه الصفحة نظرة عامة أساسية حول طريقة إنشاء شريط تحميل متكيف اللون وسهل الاستخدام باستخدام العنصر <progress>.

أود في هذه المشاركة أن أشارككم التفكير حول كيفية إنشاء ألوان تكييفية شريط تحميل يمكن الوصول إليه باستخدام عنصر <progress> جرّب العرض التوضيحي وعرض المصدر!

تم طرح إصدار تجريبي للإضاءة الداكنة والظلمة وغير المحددة والتزايد والاكتمال على Chrome.

إليك نسخة من هذه المشاركة على YouTube إذا كنت تفضّل ذلك:

نظرة عامة

تشير رسالة الأشكال البيانية <progress> يقدم ملاحظات مرئية وصوتية للمستخدمين حول الإكمال. هذا النمط تعد الملاحظات المرئية ذات قيمة لسيناريوهات مثل: التقدم من خلال نموذج، أو عرض تنزيل أو تحميل معلومات، أو حتى إظهار أن مستوى التقدم غير معروف ولكن لا يزال العمل نشطًا.

نجح تحدي واجهة المستخدم الرسومية هذا مع عنصر HTML <progress> الحالي لتوفير بعض الجهد في تسهيل إمكانية الوصول. تشير رسالة الأشكال البيانية تدفع الألوان والتخطيطات حدود التخصيص للعنصر المضمن، تحديث المكون وجعله يتناسب بشكل أفضل مع أنظمة التصميم.

علامات التبويب الفاتحة والداكنة في كل متصفح توفر 
    نظرة عامة على الرمز التكيُّفي من أعلى إلى أسفل: 
    Safari وFirefox وChrome.
العرض التوضيحي يظهر في متصفحات Firefox وSafari وiOS Safari Chrome وAndroid Chrome في المخطّطات الفاتحة والداكنة.

Markup

اخترتُ التفاف العنصر <progress> في <label> لذلك يمكنني تخطي سمات العلاقة الصريحة لصالح إشارة ضمنية العلاقة لقد وصفت أيضًا عنصرًا رئيسيًا متأثرًا بحالة التحميل، لذا فإن الشاشة يمكن لتقنيات القارئ من نقل هذه المعلومات إلى المستخدم.

<progress></progress>

إذا لم يتوفر value، يصبح تقدم العنصر غير محدد. يتم ضبط السمة max تلقائيًا على 1، وبالتالي يتراوح مستوى التقدّم بين 0 و1. إعداد max على 100، على سبيل المثال، يتم تعيين النطاق على 0-100. اخترتُ البقاء في المنزل و 1، مما يؤدي إلى ترجمة قيم التقدم إلى 0.5 أو 50٪.

مستوى التقدم المحرز في تصنيف

في العلاقة الضمنية، يتم التفاف عنصر التقدم بتصنيف مثل هذا:

<label>Loading progress<progress></progress></label>

اخترت في العرض التوضيحي تضمين تصنيف قارئات الشاشة فقط. ويتم ذلك من خلال التفاف نص التصنيف في <span> وتطبيق بعض الأنماط. بحيث يكون خارج الشاشة بشكل فعال:

<label>
  <span class="sr-only">Loading progress</span>
  <progress></progress>
</label>

مع CSS المصاحب التالي من WebAIM:

.sr-only {
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
}

لقطة شاشة لأدوات مطوري البرامج تكشف عن عنصر &quot;جاهز للشاشة&quot; فقط

المنطقة المتأثرة بتقدم التحميل

إذا كانت لديك رؤية صحية، قد يكون من السهل ربط مؤشر للتقدّم مع العناصر ومناطق الصفحة ذات الصلة، ولكن بالنسبة للمستخدمين ذوي العجز البصري، واضح للغاية. يمكنك تحسين ذلك من خلال إسناد aria-busy إلى العنصر الأعلى الذي يتغير عند اكتمال التحميل. بالإضافة إلى ذلك، يجب توضيح العلاقة بين مستوى التقدّم ومنطقة التحميل. مع aria-describedby

<main id="loading-zone" aria-busy="true">
  …
  <progress aria-describedby="loading-zone"></progress>
</main>

من JavaScript، يمكنك التبديل بين aria-busy وtrue في بداية المهمة false بمجرد الانتهاء.

إضافات سمات Aria

على الرغم من أنّ الدور الضمني لعنصر <progress> هو progressbar، لقد أشرت إلى ذلك بشكلٍ صريح في المتصفحات التي تفتقر إلى هذا الدور الضمني لقد أضفتُ أيضًا السمة indeterminate لوضع العنصر بشكل صريح في حالة غير معروفة، وهي أكثر وضوحًا من ملاحظة أنّ العنصر لا يحتوي على مجموعة value.

<label>
  Loading 
  <progress 
    indeterminate 
    role="progressbar" 
    aria-describedby="loading-zone"
    tabindex="-1"
  >unknown</progress>
</label>

استخدام tabindex="-1" لجعل عنصر التقدم قابلاً للتركيز من JavaScript. هذا مهم قارئ الشاشة، نظرًا لأن إعطاء تركيز التقدم كتغييرات في التقدم، سيُعلن للمستخدم عن مدى التقدم الذي تم تحديثه.

الأنماط

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

التنسيق

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

تنسيق <progress>

يتم ترك عرض عنصر التقدم دون تغيير بحيث يمكن أن يتقلص ويزداد مع المساحة المطلوبة في التصميم. يتم تجريد الأنماط المضمنة من خلال إعداد appearance وborder على none. يتم ذلك حتى يمكن تمييز عبر المتصفحات، حيث إن كل متصفح له أنماطه الخاصة العنصر.

progress {
  --_track-size: min(10px, 1ex);
  --_radius: 1e3px;

  /*  reset  */
  appearance: none;
  border: none;

  position: relative;
  height: var(--_track-size);
  border-radius: var(--_radius);
  overflow: hidden;
}

تستخدم قيمة 1e3px لـ _radius الرقم العلمي العلامة للتعبير عن رقم كبير ولذلك يتم تقريب border-radius دائمًا. تعادل 1000px أحب استخدام هذا لأن هدفي هو استخدام قيمة كبيرة بما يكفي يمكنني ضبط السياسة ونسيانها (وتكون الكتابة أقصر من 1000px). من المهم أيضًا من السهل تكبيره أكثر إذا لزم الأمر: ما عليك سوى تغيير الرقم 3 إلى 4، وبالتالي فإن قيمة 1e4px ما يعادل 10000px.

يُستخدم overflow: hidden وكان أسلوبًا خلابيًا. لقد صنع بعض الأمور سهلة، مثل عدم الحاجة إلى تمرير قيم border-radius إلى وتتبع عناصر التعبئة وتتبعها؛ ولكنه يعني أيضًا عدم وجود أي أطفال للتقدم يمكن أن تعيش خارج العنصر. تكرار آخر لهذا التقدّم المخصّص بدون overflow: hidden وقد يؤدي ذلك إلى فتح بعض فرص الرسوم المتحركة أو حالات إكمال أفضل.

اكتمل مستوى التقدم

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

progress:not([max])[value="1"]::before,
progress[max="100"][value="100"]::before {
  content: "";
  
  position: absolute;
  inset-block: 0;
  inset-inline: auto 0;
  display: flex;
  align-items: center;
  padding-inline-end: max(calc(var(--_track-size) / 4), 3px);

  color: white;
  font-size: calc(var(--_track-size) / 1.25);
}

لقطة شاشة لشريط التحميل عند نسبة 100% وتُظهر علامة اختيار في نهايته

اللون

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

أنماط المتصفح الفاتحة والداكنة

لتفعيل أحد عناصر <progress> التكيُّفية ذات اللونَين الداكن والفاتح على موقعك الإلكتروني، color-scheme هو كل ما هو مطلوب.

progress {
  color-scheme: light dark;
}

لون معبأ لمستوى التقدّم في موقع واحد

لتلوين عنصر <progress>، استخدِم accent-color.

progress {
  accent-color: rebeccapurple;
}

لاحظ تغير لون خلفية المقطع الصوتي من فاتح إلى داكن حسب accent-color يضمن المتصفح تباينًا مناسبًا وأنيقًا.

ألوان فاتحة وغامقة مخصّصة بالكامل

ضبط خاصيتين مخصّصتين في العنصر <progress>، واحدة للون المسار والآخر للون تقدم المسار. داخل prefers-color-scheme استعلام وسائط، قم بتوفير قيم لون جديدة للمسار وتتبع التقدم.

progress {
  --_track: hsl(228 100% 90%);
  --_progress: hsl(228 100% 50%);
}

@media (prefers-color-scheme: dark) {
  progress {
    --_track: hsl(228 20% 30%);
    --_progress: hsl(228 100% 75%);
  }
}

أنماط التركيز

أعطينا العنصر في وقت سابق فهرس علامة تبويب سالبة بحيث يمكن التركيز. استخدام :focus-visible إلى يمكنك تخصيص التركيز لتفعيل نمط حلقة التركيز الأذكى. باستخدام هذا، الماوس النقر والتركيز على حلقة التركيز، ولكن ستظهر نقرات لوحة المفاتيح. تشير رسالة الأشكال البيانية يتطرق فيديو YouTube إلى هذا الموضوع بشكل أكثر تعمقًا يستحق المراجعة.

progress:focus-visible {
  outline-color: var(--_progress);
  outline-offset: 5px;
}

لقطة شاشة لشريط التحميل تظهر حوله حلقة تركيز. جميع الألوان متطابقة.

الأنماط المخصّصة على المتصفّحات المختلفة

يمكنك تخصيص الأنماط عن طريق تحديد أجزاء عنصر <progress> التي الكشف عن الصور في المتصفح. استخدام عنصر التقدم هو علامة واحدة، لكنها تتكون من بعض العناصر الثانوية التي يتم عرضها عبر محددات CSS الزائفة. أدوات مطوري البرامج في Chrome سيعرض لك هذه العناصر في حال تفعيل الإعداد:

  1. انقر بزر الماوس الأيمن على صفحتك واختَر فحص العنصر لإظهار "أدوات مطوّري البرامج".
  2. النقر على رمز ترس "الإعدادات" (Settings) في أعلى يسار نافذة "أدوات مطوّري البرامج"
  3. ضمن العنوان Elements، ابحث عن الخيار إظهار ظل وكيل المستخدم وفعِّله DOM.

لقطة شاشة توضّح الموضع في &quot;أدوات مطوري البرامج&quot; حيث يجب إظهار shadow DOM لوكيل المستخدم.

نمطا Safari وChromium

تعرض المتصفحات المستندة إلى WebKit مثل Safari وChromium ::-webkit-progress-bar و::-webkit-progress-value، ويسمحان لمجموعة فرعية من خدمة مقارنة الأسعار (CSS) التي سيتم استخدامها. في الوقت الحالي، يمكنك ضبط background-color باستخدام السمات المخصّصة. أنشأناها سابقًا، والتي تتكيّف مع الإضاءة والظلام.

/*  Safari/Chromium  */
progress[value]::-webkit-progress-bar {
  background-color: var(--_track);
}

progress[value]::-webkit-progress-value {
  background-color: var(--_progress);
}

لقطة شاشة تعرض العناصر الداخلية لعنصر التقدّم.

أنماط Firefox

لا يعرض Firefox سوى أداة الاختيار الصورية ::-moz-progress-bar على العنصر <progress> هذا يعني أيضًا أننا لا نستطيع تلوين المسار مباشرةً.

/*  Firefox  */
progress[value]::-moz-progress-bar {
  background-color: var(--_progress);
}

لقطة شاشة من متصفّح Firefox ومكان العثور على أجزاء عنصر التقدّم

لقطة شاشة لقسم Debugging Corner حيث يقوم Safari وiOS Safari 
  يظهر شريط التحميل أثناء العمل على كل من Firefox وChrome وChrome على Android.

يُرجى ملاحظة أنّ Firefox يحتوي على مجموعة لون مسار من accent-color بينما iOS Safari له مسار أزرق فاتح. وينطبق الأمر نفسه في الوضع الداكن، لأنّ فايرفوكس يتضمّن مسارًا مظلمًا ولكن وليس اللون المخصص الذي قمنا بتعيينه، كما أنها تعمل في المتصفحات المستندة إلى Webkit.

Animation

أثناء العمل باستخدام محددات زائفة مدمجة في المتصفح، غالبًا ما يكون مع مجموعة من خصائص CSS المسموح بها.

تحريك المقطع الصوتي بملء الشاشة

إن إضافة انتقال إلى inline-size من إجمالي يعمل عنصر التقدم مع Chromium ولكن ليس مع Safari. يفعل فايرفوكس أيضًا لا تستخدم خاصية النقل على ::-moz-progress-bar.

/*  Chromium Only 😢  */
progress[value]::-webkit-progress-value {
  background-color: var(--_progress);
  transition: inline-size .25s ease-out;
}

جارٍ تحريك حالة :indeterminate

أصبحت هنا أكثر إبداعًا حتى أتمكن من تقديم رسوم متحركة. عنصر زائف في Chromium ويتم تطبيق تدرج يظهر بشكل متحرك المقدمة لجميع المتصفحات الثلاثة.

الخصائص المخصصة

تناسب الخصائص المخصصة العديد من الأمور، ولكن أحد تفضيلاتي هو ببساطة إعطاء اسم لقيمة CSS ذات مظهر سحري. تعد المتابعة نوعًا ما معقد linear-gradient، ولكن باسم لطيف. ويمكن فهم الغرض من هذه الميزة وحالات استخدامها بوضوح.

progress {
  --_indeterminate-track: linear-gradient(to right,
    var(--_track) 45%,
    var(--_progress) 0%,
    var(--_progress) 55%,
    var(--_track) 0%
  );
  --_indeterminate-track-size: 225% 100%;
  --_indeterminate-track-animation: progress-loading 2s infinite ease;
}

ستساعد الخصائص المخصصة أيضًا في بقاء التعليمات البرمجية غير مكتملة لأنه لا يمكننا تجميع أدوات الاختيار هذه الخاصة بالمتصفح معًا.

الإطارات الرئيسية

الهدف هو رسم متحرك غير محدود يتنقل ذهابًا وإيابًا. البداية والنهاية الإطارات الرئيسية في CSS. ولن تحتاج سوى إلى إطار رئيسي واحد، أي الإطار الرئيسي الأوسط في 50%، لإنشاء صورة متحركة تعود إلى حيث بدأت مرة أخرى وتكرارًا!

@keyframes progress-loading {
  50% {
    background-position: left; 
  }
}

استهداف كل متصفح

هناك بعض المتصفحات التي لا تسمح بإنشاء عناصر زائفة على <progress> العنصر نفسه أو يسمح بتحريك شريط التقدم. توافق المزيد من المتصفحات مقطعًا متحركًا بدلاً من عنصر زائف، لذلك قمت بالترقية من العناصر الزائفة قاعدة وإلى أشرطة متحركة.

عنصر زائف في Chromium

لا يسمح Chromium بالعنصر الزائف: ::after المستخدم مع موضع لتغطية العنصر. يتم استخدام الخصائص المخصصة غير المحددة، تعمل الرسوم المتحركة للأمام بشكل جيد للغاية.

progress:indeterminate::after {
  content: "";
  inset: 0;
  position: absolute;
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}
شريط التقدّم في متصفّح Safari

وبالنسبة إلى Safari، يتمّ تطبيق الخصائص المخصّصة والرسوم المتحركة على شريط التقدّم لعنصر زائف:

progress:indeterminate::-webkit-progress-bar {
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}
شريط التقدّم في Firefox

بالنسبة إلى فايرفوكس، تنطبق أيضًا الخصائص المخصصة والرسوم المتحركة على شريط التقدّم لعنصر زائف:

progress:indeterminate::-moz-progress-bar {
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}

JavaScript

تؤدي لغة JavaScript دورًا مهمًا مع العنصر <progress>. يتحكم في هذه الميزة القيمة المرسلة إلى العنصر وتضمن وجود معلومات كافية في مستند لقارئات الشاشة.

const state = {
  val: null
}

يعرض العرض التوضيحي أزرارًا للتحكم في التقدم؛ تم تعديل state.val ثم استدعينا دالة لتحديث نموذج كائن المستند (DOM)

document.querySelector('#complete').addEventListener('click', e => {
  state.val = 1
  setProgress()
})

setProgress()

هذه الدالة هي المكان الذي يتم فيه تنسيق تجربة المستخدم/واجهة المستخدم. يمكنك البدء بإنشاء setProgress(). لا حاجة إلى أي معلَمات نظرًا لأن بإمكانه الوصول إلى كائن state وعنصر التقدّم ومنطقة <main>.

const setProgress = () => {
  
}

ضبط حالة التحميل في منطقة <main>

استنادًا إلى ما إذا كان مستوى التقدّم مكتملاً أم لا، تتوفّر <main> ذات الصلة يحتاج إلى تعديل aria-busy السمة:

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)
}

محو السمات إذا كان مقدار التحميل غير معروف

في حال كانت القيمة غير معروفة أو بدون ضبط، null في هذا الاستخدام، أزِل value aria-valuenow. سيؤدي ذلك إلى تحويل <progress> إلى قيمة غير محددة.

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }
}

إصلاح المسائل الحسابية الأعداد العشرية في JavaScript

نظرًا لأنني اخترت الالتزام بحد أقصى افتراضي للتقدم 1، فإن العرض التوضيحي تستخدم دوال زيادة القيم والتناقص العمليات الحسابية العشرية. وغير ذلك اللغات، ليست دائمًا رائعة في ذلك. إليك دالة roundDecimals() التي ستزيل المبالغ الزائدة من العملية الحسابية. النتيجة:

const roundDecimals = (val, places) =>
  +(Math.round(val + "e+" + places)  + "e-" + places)

تقريب القيمة بحيث يمكن تقديمها وتكون واضحة:

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"
}

ضبط قيمة لبرامج قراءة الشاشة وحالة المتصفّح

يتم استخدام القيمة في ثلاثة مواقع في DOM:

  1. السمة value للعنصر <progress>.
  2. السمة aria-valuenow.
  3. محتوى النص الداخلي لـ <progress>
const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"

  progress.value = val
  progress.setAttribute('aria-valuenow', valPercent)
  progress.innerText = valPercent
}

إعطاء تركيز التقدم

من خلال تعديل القيم، سيرى المستخدمون المبصرون التغيير في مستوى التقدّم، ولكن شاشتهم لم يتم بعد إعلان التغيير لمستخدمي القرّاء. ركِّز <progress> وسيعلن المتصفّح عن التحديث.

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"

  progress.value = val
  progress.setAttribute('aria-valuenow', valPercent)
  progress.innerText = valPercent

  progress.focus()
}

لقطة شاشة لتطبيق Voice Over بنظام التشغيل Mac OS 
  قراءة مستوى تقدم شريط التحميل للمستخدم.

الخاتمة

الآن بعد أن تعرّفت على كيفية إجراء ذلك، كيف يمكنك‽ 🙂

هناك بالتأكيد بعض التغييرات التي أود إجراؤها إذا توفرت فرصة أخرى. أعتقد أنّ هناك مجالاً لحذف المكوِّن الحالي، وهناك مجال لمحاولة إنشاء مكوّن بدون قيود النمط الصورية الخاصة بعنصر <progress>. الأمر يستحق الاستكشاف!

يمكننا تنويع أساليبنا وتعلُّم جميع طرق إنشاء المحتوى على الويب.

أنشئ عرضًا توضيحيًا وأضيف روابط تغريدة إليّ. انتقِل إلى قسم الريمكسات في المنتدى أدناه.

ريمكسات من إنشاء المنتدى