إنشاء مكوّن علامات التبويب

نظرة عامة أساسية حول كيفية إنشاء مكوّن علامات تبويب مشابه لما هو متوفّر في تطبيقات iOS وAndroid

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

العرض التوضيحي

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

نظرة عامة

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

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

يتسم هذا الكولاج بالفوضى بسبب التنوع الكبير في الأنماط التي طبّقتها الويب على مفهوم المكوّن.
صورة مجمّعة لأنماط تصميم الويب لمكوّنات علامات التبويب على مدار آخر 10 سنوات

Web Tactics

بوجهٍ عام، تبيّن لي أنّه من السهل جدًا إنشاء هذا المكوّن، وذلك بفضل بعض الميزات المهمة لمنصّة الويب:

  • scroll-snap-points لتفاعلات سلسة بين التمرير السريع ولوحة المفاتيح مع مواضع توقف مناسبة للانتقال
  • الروابط لصفحات في التطبيق من خلال تجزئات عناوين URL لسماح المتصفّح بربط الانتقال إلى صفحات في التطبيق ومشاركتها
  • إتاحة قارئ الشاشة باستخدام ترميز العنصرَين <a> وid="#hash"
  • prefers-reduced-motion لتفعيل الانتقالات باستخدام التمويه وميزة التنقّل الفوري في الصفحة
  • ميزة الويب @scroll-timeline في المسودة للتمييز تحت النص ديناميكيًا و تغيير لون علامة التبويب المحدّدة

رمز HTML

في الأساس، تتمثل تجربة المستخدم هنا في النقر على رابط، وجعل عنوان URL يمثّل حالة الصفحة المُدمجة، ثم مشاهدة تعديل منطقة المحتوى أثناء انتقال المتصفّح إلى العنصر المطابق.

هناك بعض عناصر المحتوى الهيكلية: الروابط و:target. نحتاج إلى قائمة بالروابط التي تناسبها <nav> وقائمة بعناصر <article> التي تناسبها <section>. ستتطابق كلّ علامة تجزئة للرابط مع قسم معيّن، مما يتيح للمتصفّح الانتقال إلى المحتوى من خلال الربط.

يتم النقر على زر رابط، ما يؤدي إلى تمرير المحتوى الذي يتم التركيز عليه

على سبيل المثال، يؤدي النقر على رابط إلى تركيز المقالة :target تلقائيًا في Chrome 89، بدون الحاجة إلى JavaScript. يمكن للمستخدم بعد ذلك الانتقال إلى أعلى أو أسفل محتوى المقالة باستخدام جهاز الإدخال كالمعتاد. وهو محتوى مكمّل، كما هو موضّح في علامة الترميز.

لقد استخدمت الترميز التالي لتنظيم علامات التبويب:

<snap-tabs>
  <header>
    <nav>
      <a></a>
      <a></a>
      <a></a>
      <a></a>
    </nav>
  </header>
  <section>
    <article></article>
    <article></article>
    <article></article>
    <article></article>
  </section>
</snap-tabs>

يمكنني إنشاء علاقات بين العنصرَين <a> و<article> باستخدام موقعَي href وid على النحو التالي:

<snap-tabs>
  <header>
    <nav>
      <a href="#responsive"></a>
      <a href="#accessible"></a>
      <a href="#overscroll"></a>
      <a href="#more"></a>
    </nav>
  </header>
  <section>
    <article id="responsive"></article>
    <article id="accessible"></article>
    <article id="overscroll"></article>
    <article id="more"></article>
  </section>
</snap-tabs>

بعد ذلك، ملأت المقالات بكميات مختلطة من نص Lorem ipsum، والروابط بعناوين تتحلى بطول ومقدار مختلطَين من الصور. بعد الحصول على المحتوى المطلوب، يمكننا البدء في تصميمه.

التنسيقات التي تتيح الانتقال للأعلى أو للأسفل

هناك 3 أنواع مختلفة من مناطق الانتقال في هذا المكوّن:

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

هناك نوعان مختلفان من العناصر المعنيّة بالتمرير:

  1. نافذة
    صندوق بقياسات محدّدة يتضمّن أسلوب السمة overflow.
  2. سطح عرض كبير جدًا
    في هذا التنسيق، تكون حاويات القوائم هي: روابط التنقّل والمقالات في الأقسام ومحتوى المقالات.

التنسيق <snap-tabs>

تنسيق المستوى الأعلى الذي اخترته هو flex (Flexbox). لقد ضبطتُ الاتجاه على column، وبالتالي يتم ترتيب العنوان والقسم عموديًا. هذه هي نافذة التمرير الأولى، وهي تخفي كل شيء باستخدام overflow hidden. سيتم قريبًا استخدام ميزة التمرير السريع في العنوان وال القسم، كمناطق فردية.

HTML
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
CSS
  snap-tabs {
  display: flex;
  flex-direction: column;

  /* establish primary containing box */
  overflow: hidden;
  position: relative;

  & > section {
    /* be pushy about consuming all space */
    block-size: 100%;
  }

  & > header {
    /* defend against 
needing 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }

بالرجوع إلى الرسم البياني الملوّن المكوّن من 3 لفات:

  • أصبح <header> جاهزًا الآن ليكون حاوية التمرير (باللون الوردي).
  • تم تجهيز <section> ليكون حاوية التمرير (باللون الأزرق).

تساعدنا الإطارات التي أبرزتها أدناه باستخدام VisBug في الاطّلاع على النوافذ التي أنشأتها حاويات التمرير.

تحتوي عناصر العنوان والقسم على تراكبات باللون الوردي الساخن، ما يحدّد المساحة التي تشغلها في المكوّن.

تنسيق علامات التبويب <header>

التنسيق التالي مشابه تقريبًا: أستخدِم flex لإنشاء ترتيب عمودي.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

يجب أن ينتقل .snap-indicator أفقيًا مع مجموعة الروابط، ويساعد .snap-indicator تنسيق العنوان هذا في إعداد هذه المرحلة. لا تتضمّن هذه الصفحة أي عناصر ذات موضع مطلق.

تحتوي عناصر nav وspan.indicator على تراكبات باللون الوردي الساخن، ما يحدّد المساحة التي تشغلها في المكوّن.

بعد ذلك، أنماط الانتقال إلى الأسفل أو الأعلى. تبيّن لنا أنّه يمكننا مشاركة أنماط التمرير بين منطقتَي التمرير الأفقيتَين (العنوان والقسم)، لذا أنشأتُ فئة أداة، .scroll-snap-x.

.scroll-snap-x {
  /* browser decide if x is ok to scroll and show bars on, y hidden */
  overflow: auto hidden;
  /* prevent scroll chaining on x scroll */
  overscroll-behavior-x: contain;
  /* scrolling should snap children on x */
  scroll-snap-type: x mandatory;

  @media (hover: none) {
    scrollbar-width: none;

    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
  }
}

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

تنسيق <nav> لعنوان علامات التبويب

يجب عرض روابط التنقّل في سطر واحد بدون فواصل بين الأسطر، ويجب أن تكون في منتصف الشاشة عموديًا، ويجب أن ينطبق كل رابط على حاوية التمرير السريع. Swift work for 2021 CSS!

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

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

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

تنسيق علامات التبويب <section>

هذا القسم هو عنصر مرن ويجب أن يكون المستهلك الرئيسي للمساحة. يجب أيضًا إنشاء أعمدة لوضع المقالات فيها. مرة أخرى، نشكرك على سرعة العمل في ما يتعلّق بخدمات مقارنة الأسعار لعام 2021. يمدّد block-size: 100% هذا العنصر لملء العنصر الأساسي قدر الإمكان، ثمّ ينشئ سلسلة من الأعمدة التي تبلغ 100% عرض العنصر الأساسي لتصميمه الخاص. تعمل النسب المئوية بشكلٍ رائع في هذه الحالة لأنّنا وضعنا قيودًا صارمة على العنصر الرئيسي.

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

يبدو الأمر كما لو كنا نقول "توسيع المحتوى عموديًا قدر الإمكان، بطريقة قوية" (تذكَّر العنوان الذي ضبطناه على flex-shrink: 0: فهو دفاع ضد دفعة التوسيع هذه)، ما يضبط ارتفاع الصف لمجموعة من الأعمدة بالارتفاع الكامل. يطلب أسلوب auto-flow من الشبكة ترتيب العناصر الثانوية دائمًا في خط أفقي بدون التفاف، وهو ما نريد أن يحدث بالضبط، أي أن تتجاوز العناصر الثانوية النافذة الرئيسية.

تحتوي عناصر المقالة على تراكب باللون الوردي الساخن، ما يحدّد المساحة التي تشغلها في المكوّن ومكان تداخلها

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

تنسيق علامات التبويب <article>

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

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

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

يتضمّن عنصر المقالة وعناصره الفرعية تراكبات بلون زهري فاتح، ما يحدّد المساحة التي يشغلونها في المكوّن والاتجاه الذي يتدفّقون فيه.

المقالة هي عنصر ثانوي في الشبكة، ويتم تحديد حجمها مسبقًا ليكون مساحة viewport التي نريد توفير تجربة مستخدم للانتقال فيها. وهذا يعني أنّني لا أحتاج إلى أي تنسيقات للارتفاع أو العرض هنا، بل عليّ فقط تحديد كيفية تجاوز المحتوى للحدود. لقد ضبطت overflow-y على auto، ثم أيضًا حصرت تفاعلات التمرير باستخدام السمة overscroll-behavior السهلة الاستخدام.

ملخّص عن 3 مناطق قابلة للتمرير

في ما يلي، اخترت "عرض أشرطة التمرير دائمًا" في إعدادات النظام. أعتقد أنّه من المهم جدًا أن يعمل التنسيق مع تفعيل هذا الإعداد، كما أنّه من المهم جدًا بالنسبة إليّ مراجعة التنسيق وتنسيق الانتقال إلى الأسفل أو الأعلى.

تم ضبط أشرطة التمرير الثلاثة للظهور، وهي تستهلك الآن مساحة التنسيق، ولا يزال المكوّن يبدو رائعًا.

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

يمكن أن تساعدنا "أدوات مطوّري البرامج" في تصور ذلك:

تحتوي مناطق التمرير على تراكبات أداة شبكة وصندوق مرن، ما يحدّد المساحة التي تشغلها في المكوّن والاتجاه الذي تتدفق فيه
أداة Chromium Devtools، تعرض تنسيق عنصر التنقّل في المربّع المرن المملوء بعناصر الربط، وتنسيق قسم الشبكة المملوء بعناصر المقالات، وعناصر المقالات المملوءة بفقرات وعنصر عنوان

أن تكون تنسيقات التمرير كاملة: أن تكون قابلة للالتقاط وقابلة للربط برابط لصفحة في التطبيق وقابلة للاستخدام باستخدام لوحة المفاتيح أساس متين لتحسينات تجربة المستخدم والأناقة والسعادة

تسليط الضوء على الميزات

عند التمرير، تحافظ العناصر المُثبَّتة في الخلفية على وضعها المُقفَل أثناء تغيير الحجم. وهذا يعني أنّه لن يحتاج JavaScript إلى عرض أي محتوى عند تدوير الجهاز أو تغيير حجم المتصفّح. يمكنك تجربة ذلك في وضع الجهاز في أدوات مطوّري البرامج في Chromium من خلال اختيار أي وضع آخر غير متوافق مع جميع الأجهزة، ثم تغيير حجم إطار الجهاز. يبقى العنصر مرئيًا ومُقفَلاً مع محتواه. وقد أصبح هذا الإجراء متاحًا منذ أن عدّل فريق Chromium عملية التنفيذ لتتوافق مع المواصفات. يمكنك الاطّلاع على مشاركة مدونة حول هذا الموضوع.

Animation

الهدف من عمل الرسوم المتحركة هنا هو ربط التفاعلات بوضوح بملاحظات واجهة المستخدم. يساعد ذلك في توجيه المستخدم أو مساعدته على التنقّل في المحتوى (على أمل) بسلاسة. سأضيف الحركة بهدف وبصفة مشروطة. يمكن للمستخدمين الآن تحديد إعدادات الحركات المفضّلة لديهم في نظام التشغيل، ويسعدنا كثيرًا الاستجابة لإعداداتهم المفضّلة في واجهاتنا.

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

سلوك التمرير

هناك فرصة لتحسين سلوك الحركة لكلّ من :target و element.scrollIntoView(). وتكون القيمة التلقائية هي "فورية". يضبط المتصفّح فقط موضع الانتقال. ماذا لو أردنا الانتقال إلى موضع التمرير هذا بدلاً من وميضه هناك؟

@media (prefers-reduced-motion: no-preference) {
  .scroll-snap-x {
    scroll-behavior: smooth;
  }
}

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

مؤشر علامات التبويب

الغرض من هذا المخطّط المتحرك هو المساعدة في ربط المؤشر بحالة المحتوى. لقد قرّرتُ استخدام أنماط انتقال الألوان border-bottom للمستخدمين الذين يفضّلون استخدام الصور التي تتضمّن حركة أقل، واستخدام انتقال سلس مرتبط بالتمرير + تأثير محو للألوان للمستخدمين الذين لا يمانعون استخدام الصور التي تتضمّن حركة.

في Chromium Devtools، يمكنني تبديل الخيار المفضّل وعرض أسلوبَي التحوّل المختلفَين. لقد استمتعت كثيرًا بإنشاء هذا المحتوى.

@media (prefers-reduced-motion: reduce) {
  snap-tabs > header a {
    border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
    transition: color .7s ease, border-color .5s ease;

    &:is(:target,:active,[active]) {
      color: var(--text-active-color);
      border-block-end-color: hsl(var(--accent));
    }
  }

  snap-tabs .snap-indicator {
    visibility: hidden;
  }
}

أخفي الرمز .snap-indicator عندما يفضّل المستخدم استخدام ميزة "تقليل الحركة" لأنّه لم أعد أحتاج إليه. بعد ذلك، أستبدله بأنماط border-block-end و transition. لاحظ أيضًا في تفاعل علامات التبويب أنّ عنصر التنقّل النشط ليس لديه فقط تمييزًا للعلامة التجارية تحتها خط، ولكن لون النص أيضًا أغمق. يتميز العنصر النشط بتباين أعلى في لون النص وإضاءة سفلية ساطعة.

بإضافة بضعة أسطر إضافية من CSS، يمكنك أن تجعل المستخدم يشعر بأنّه تم أخذ رغباته في الاعتبار (بمعنى أنّه نحترم بعناية إعدادات السرعة المفضّلة لديه). أحبّ ذلك.

@scroll-timeline

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

const { matches:motionOK } = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
);

أولاً، أتحقّق من إعدادات المستخدم المفضّلة للحركة من JavaScript. إذا كانت نتيجة هذا الإجراء هي false، ما يعني أنّ المستخدم يفضّل التأثيرات المتحركة المنخفضة، لن نعرض أي من تأثيرات الربط بالانتقال إلى الأعلى أو الأسفل.

if (motionOK) {
  // motion based animation code
}

في وقت كتابة هذه السطور، لا تتوفّر متصفّحات متوافقة مع @scroll-timeline. وهي مسودة مواصفات تتضمّن فقط عمليات التنفيذ التجريبية. ومع ذلك، يحتوي على polyfill أستخدمه في العرض التجريبي هذا.

ScrollTimeline

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

const sectionScrollTimeline = new ScrollTimeline({
  scrollSource: tabsection,  // snap-tabs > section
  orientation: 'inline',     // scroll in the direction letters flow
  fill: 'both',              // bi-directional linking
});

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

tabindicator.animate({
    transform: ...,
    width: ...,
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

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

الإطارات الرئيسية الديناميكية

هناك طريقة قوية جدًا وواضحة في CSS لإنشاء رسوم متحركة باستخدام @scroll-timeline، ولكنّ الرسوم المتحركة التي اخترت تنفيذها كانت ديناميكية جدًا. لا تتوفّر طريقة للتبديل بين auto العرض، ولا تتوفّر طريقة لإنشاء عدد من اللقطات الرئيسية ديناميكيًا استنادًا إلى طول الأطفال.

ومع ذلك، تعرف JavaScript كيفية الحصول على هذه المعلومات، لذا سنكرّر children بأنفسنا ونحصل على القيم المحسوبة أثناء التشغيل:

tabindicator.animate({
    transform: [...tabnavitems].map(({offsetLeft}) =>
      `translateX(${offsetLeft}px)`),
    width: [...tabnavitems].map(({offsetWidth}) =>
      `${offsetWidth}px`)
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

لكل tabnavitem، أزِل بنية موضع offsetLeft وأعِد سلسلة تستخدمه كقيمة translateX. يؤدي ذلك إلى إنشاء 4 لقطات رئيسية للتحويل في المقطع المتحرك. ويتم إجراء الأمر نفسه بالنسبة إلى العرض، حيث يُطلب من كل إطار معرفة عرضه الديناميكي ثم يتم استخدامه كقيمة للإطار الرئيسي.

في ما يلي مثال على الإخراج استنادًا إلى الخطوط والإعدادات المفضّلة للمتصفّح:

الإطارات الرئيسية لدالة TranslateX:

[...tabnavitems].map(({offsetLeft}) =>
  `translateX(${offsetLeft}px)`)

// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]

الإطارات الرئيسية للعرض:

[...tabnavitems].map(({offsetWidth}) =>
  `${offsetWidth}px`)

// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]

لتلخيص الاستراتيجية، سيتم الآن عرض مؤشر علامة التبويب بشكل متحرك على 4 صور رئيسية استنادًا إلى موضع التمرير السريع لأداة التمرير في القسم. تُنشئ نقاط الربط تمييزًا واضحًا بين اللقطات الرئيسية وتُضفي شعورًا بالتناغم على المشهد المتحرك.

يتم عرض علامة التبويب النشطة وعلامة التبويب غير النشطة مع تراكب VisBug الذي يعرض نتائج التباين المقبولة لكلاهما.

يدير المستخدم الصورة المتحركة من خلال تفاعله، ويلاحظ تغيُّر العرض ووضع المؤشر من قسم إلى آخر، وتتبُّع بدقة من خلال الانتقال للأعلى أو للأسفل.

ربما لم تلاحظ ذلك، ولكنّني فخور جدًا بتغيير اللون عند تحديد عنصر التنقّل المميّز.

يظهر اللون الرمادي الفاتح غير المحدَّد بشكلٍ أكثر وضوحًا عندما يكون للعنصر المميّز تباينًا أعلى. من الشائع تغيير لون النص، مثل عند التمرير فوقه وعند اختياره، ولكن من المستوى التالي تغيير هذا اللون عند الانتقال للأسفل أو للأعلى، بالتزامن مع مؤشر ال underline.

إليك كيفية إجراء ذلك:

tabnavitems.forEach(navitem => {
  navitem.animate({
      color: [...tabnavitems].map(item =>
        item === navitem
          ? `var(--text-active-color)`
          : `var(--text-color)`)
    }, {
      duration: 1000,
      fill: 'both',
      timeline: sectionScrollTimeline,
    }
  );
});

يحتاج كل رابط للتنقّل في علامات التبويب إلى هذا المؤثر الملوّن الجديد، مع تتبُّع المخطط الزمني للانتقال إلى الأسفل نفسه المستخدَم في مؤشر ال underline. أستخدم المخطط الزمني نفسه كما في السابق: بما أنّ دور العلامة هو عرض علامة عند الانتقال للأعلى أو للأسفل، يمكننا استخدام هذه العلامة في أي نوع من الرسوم المتحركة التي نريدها. كما فعلت سابقًا، أنشأت 4 لقطات رئيسية في حلقة العرض، وأعدت الألوان.

[...tabnavitems].map(item =>
  item === navitem
    ? `var(--text-active-color)`
    : `var(--text-color)`)

// results in 4 array items, which represent 4 keyframe states
// [
  "var(--text-active-color)",
  "var(--text-color)",
  "var(--text-color)",
  "var(--text-color)",
]

يُبرز المشهد الرئيسي الذي يستخدم اللون var(--text-active-color) الرابط، ويُستخدم لون نص عادي في غير ذلك. تجعلك الحلقة المُدمجة في ذلك الأمر نسبيًا سهلة، لأنّ الحلقة الخارجية هي كل عنصر تنقّل، والحلقة الداخلية هي كل اللقطات الرئيسية الشخصية لعنصر التنقّل. أتحقّق مما إذا كان عنصر الحلقة الخارجية هو نفسه عنصر الحلقة الداخلية، وأستخدِم ذلك لمعرفة وقت اختياره.

لقد استمتعت كثيرًا بكتابة هذه المقالة. كثيرًا جدًا.

المزيد من تحسينات JavaScript

يُرجى العِلم أنّ الأساس الذي أعرضه لك هنا يعمل بدون استخدام JavaScript. مع ذلك، لنلقِ نظرة على كيفية تحسينه عندما يكون JS متاحًا.

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

window.onload = () => {
  if (location.hash) {
    tabsection.scrollLeft = document
      .querySelector(location.hash)
      .offsetLeft;
  }
}

مزامنة نهاية التمرير

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

في ما يلي كيفية انتظار نهاية الانتقال إلى الأسفل: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

عند الانتقال للأقسام، امسح مهلة القسم إذا كانت متوفّرة، وابدأ مهلة جديدة. عندما يتوقف الانتقال للأقسام، لا تُلغِ مهلة الانتظار، وشغِّل الإجراء بعد 100 ملي ثانية من الراحة. عند تشغيله، يمكنك استدعاء الدالة التي تحاول معرفة مكان توقف المستخدم.

const determineActiveTabSection = () => {
  const i = tabsection.scrollLeft / tabsection.clientWidth;
  const matchingNavItem = tabnavitems[i];

  matchingNavItem && setActiveTab(matchingNavItem);
};

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

const setActiveTab = tabbtn => {
  tabnav
    .querySelector(':scope a[active]')
    .removeAttribute('active');

  tabbtn.setAttribute('active', '');
  tabbtn.scrollIntoView();
};

يبدأ ضبط علامة التبويب النشطة من خلال محو أي علامة تبويب نشطة حاليًا، ثم منح عنصر التنقّل الوافد سمة الحالة النشطة. تتضمن الدعوة إلى scrollIntoView() تفاعلًا ممتعًا مع CSS يستحق الذكر.

.scroll-snap-x {
  overflow: auto hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  @media (prefers-reduced-motion: no-preference) {
    scroll-behavior: smooth;
  }
}

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

الخاتمة

الآن بعد أن عرفت كيف فعلت ذلك، كيف ستفعل ذلك؟ وهذا يُعدّ ميزة رائعة في بنية المكوّنات. من سيُنشئ الإصدار الأول الذي يتضمّن خانات في إطار العمل المفضّل لديه؟ 🙂

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

الريمكسات التي أنشأها المستخدمون