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

تقدّم هذه الصفحة نظرة عامة أساسية حول طريقة إنشاء شريط تحميل متكيف اللون وسهل الاستخدام باستخدام العنصر <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. اخترت البقاء ضمن الحدين 0 و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 (العناصر)، ابحث عن مربّع الاختيار Show (Show DOM) لوكيل المستخدم وفعِّله.

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

نمطا Safari وChromium

تعرض المتصفحات المستندة إلى مجموعة أدوات الويب، مثل 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 مسارًا باللون الأزرق الفاتح. الأمر نفسه في الوضع الداكن، حيث يتضمّن Firefox مسارًا داكنًا، ولكنّه ليس اللون المخصّص الذي حدّدناه، ويعمل في المتصفّحات المستندة إلى Webkit.

Animation

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

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

تعمل إضافة انتقال إلى inline-size من عنصر التقدم مع Chromium وليس مع Safari. ولا يستخدم Firefox أيضًا خاصية انتقال على ::-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

بالنسبة إلى 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)
}

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

في حال كانت القيمة غير معروفة أو بدون ضبط، ستتم إزالة السمتَين value وaria-valuenow أثناء الاستخدام null. سيؤدي ذلك إلى تحويل <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، فإن دوال الزيادة التجريبية والتقليل تستخدم الحسابات العشرية. وJavaScript واللغات الأخرى ليست دائمًا جيدة في ذلك. إليك دالة 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 
  تعرض مدى تقدّم شريط التحميل للمستخدم.

الخلاصة

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

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

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

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

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