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

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

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

إصدار تجريبي

في ما يلي إصدار YouTube من هذه المشاركة إذا كنت تفضّل الفيديوهات:

نظرة عامة

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

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

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

أساليب الويب

بشكل عام، وجدتُ هذا المكوِّن سهلاً جدًا، بفضل بعض الميزات الأساسية للنظام الأساسي للويب وهي:

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

مصدر HTML

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

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

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

على سبيل المثال، يؤدي النقر على رابط إلى التركيز تلقائيًا على مقالة :target في الإصدار 89 من Chrome، بدون الحاجة إلى 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، والروابط ذات الطول المختلط ومجموعة صور من العناوين. باستخدام المحتوى الذي تريد العمل به، يمكننا البدء في التخطيط.

تمرير التنسيقات

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

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

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

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

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

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

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

الإشارة إلى الرسم التخطيطي الملون ثلاثي التمرير:

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

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

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

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

التخطيط التالي هو نفسه تقريبًا: أستخدم المرونة لإنشاء ترتيب رأسي.

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 أفقيًا مع مجموعة الروابط، ويساعد تنسيق العنوان هذا في إعداد تلك المرحلة. لا توجد عناصر مطلقة في الموضع هنا!

تحتوي عناصر التنقل و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 لخدمة CSS لعام 2021

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

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

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

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

هذا القسم هو عنصر مرن ويجب أن يكون المستهلك الرئيسي للمساحة. كما أنه ينبغي إنشاء أعمدة للمقالات التي سيتم وضعها فيها. مرة أخرى، يمكنك استخدام swift لخدمة CSS لعام 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;
}

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

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

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

ملخّص عن 3 مناطق يمكن التنقّل فيها

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

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

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

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

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

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

إبراز الميزة

يحافظ الأطفال الموصولون بالتمرير على وضع القفل أثناء تغيير الحجم. ويعني هذا أنّ 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 كيفية الحصول على هذه المعلومات، لذا سنكرر العملية بأنفسنا ونحصل على القيم المحسوبة في وقت التشغيل:

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 التي تُظهر نتائج التباين الناجحة لكلا الخدمتين

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

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

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

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

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

يحتاج كل رابط تنقل علامة تبويب إلى هذه الرسوم المتحركة الملونة الجديدة، مع تتبع نفس المخطط الزمني للتمرير مثل مؤشر التسطير. أستخدم نفس الجدول الزمني كما في السابق: نظرًا لأن دوره يتمثل في إصدار علامة عند التمرير، يمكننا استخدام هذه العلامة في أي نوع من الرسوم المتحركة نريده. كما فعلت من قبل، أقوم بإنشاء 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. مع ذلك، لنرَ كيف يمكننا تحسينه عند توفُّر JavaScript.

الروابط لصفحات في التطبيق هي أكثر من مصطلح متوافق مع الأجهزة الجوّالة، ولكن أعتقد أنّ الغرض من الرابط لصفحة في التطبيق يتم هنا تحقيقه من خلال علامات التبويب التي تتيح لك مشاركة عنوان 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 لأداة التمرير الأفقي، تم دمج استعلام عن الوسائط ينطبق على التمرير smooth إذا كان المستخدم يتحمل الحركة. وتستطيع لغة JavaScript إجراء استدعاءات بحرية لتمرير العناصر إلى العرض، ويمكن لـ CSS إدارة تجربة المستخدم بشكل بياني. وهي مطابقة صغيرة وممتعة في بعض الأحيان.

الخلاصة

الآن بعد أن عرفت كيف فعلت ذلك، كيف ستفعل ذلك؟! هذا يجعل بعض بنية المكونات الممتعة! من سيصنع الإصدار الأول مع فتحات في إطار العمل المفضل لديه؟ 🙂

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

ريمكسات من المنتدى