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

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

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

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

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

نظرة عامة

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

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

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

أساليب الويب

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

  • 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 أنواع مختلفة من مناطق الانتقال في هذا المكوّن:

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

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

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

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

كان تخطيط المستوى الأعلى الذي اخترته مرنًا (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;
  }
}

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

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

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

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

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

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

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

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