ساخت جزء Tabs

یک نمای کلی از نحوه ساخت یک مؤلفه برگه ها مشابه آنچه در برنامه های iOS و Android یافت می شود.

در این پست می‌خواهم تفکری را در مورد ساخت یک جزء Tabs برای وب به اشتراک بگذارم که واکنش‌گرا باشد، از ورودی‌های چندگانه دستگاه پشتیبانی کند و در مرورگرها کار کند. نسخه ی نمایشی را امتحان کنید.

نسخه ی نمایشی

اگر ویدیو را ترجیح می دهید، در اینجا یک نسخه YouTube از این پست وجود دارد:

نمای کلی

زبانه ها جزء رایج سیستم های طراحی هستند اما می توانند اشکال و اشکال مختلفی داشته باشند. ابتدا برگه‌های دسکتاپ بر روی عنصر <frame> ساخته شده بودند، و اکنون مؤلفه‌های موبایل کره‌ای داریم که محتوا را بر اساس ویژگی‌های فیزیکی متحرک می‌کنند. همه آنها تلاش می کنند یک کار را انجام دهند: صرفه جویی در فضا.

امروزه، ملزومات تجربه کاربر تب ها، یک ناحیه پیمایش دکمه ای است که نمایان بودن محتوا را در یک قاب نمایشگر تغییر می دهد. بسیاری از حوزه‌های محتوای مختلف فضای یکسانی را به اشتراک می‌گذارند، اما به صورت مشروط بر اساس دکمه انتخاب‌شده در پیمایش ارائه می‌شوند.

کلاژ به دلیل تنوع زیاد سبک هایی که وب در مفهوم کامپوننت اعمال کرده است کاملاً آشفته است
مجموعه ای از سبک های طراحی وب کامپوننت برگه در 10 سال گذشته

تاکتیک های وب

در مجموع به لطف چند ویژگی مهم پلتفرم وب، ساخت این مؤلفه را بسیار ساده دیدم:

  • scroll-snap-points برای تعاملات زیبا با کشیدن انگشت و صفحه کلید با موقعیت های توقف پیمایش مناسب
  • پیوندهای عمیق از طریق هش‌های URL برای پشتیبانی از لنگر انداختن پیمایش درون صفحه و به اشتراک‌گذاری توسط مرورگر
  • پشتیبانی از صفحه‌خوان با نشانه‌گذاری عنصر <a> و id="#hash"
  • prefers-reduced-motion برای فعال کردن انتقال متقاطع و پیمایش فوری در صفحه
  • ویژگی وب پیش نویس @scroll-timeline برای خط کشی پویا و تغییر رنگ برگه انتخاب شده

HTML

اساساً، UX در اینجا این است: روی یک پیوند کلیک کنید، نشانی اینترنتی نشان دهنده وضعیت صفحه تو در تو باشد، و سپس به‌روزرسانی منطقه محتوا را در حین حرکت مرورگر به عنصر منطبق ببینید.

برخی از اعضای محتوای ساختاری در آن وجود دارد: پیوندها و :target s. ما به لیستی از پیوندها نیاز داریم که یک <nav> برای آنها عالی است، و یک لیست از عناصر <article> که یک <section> برای آنها عالی است. هر هش پیوند با یک بخش مطابقت دارد و به مرورگر اجازه می‌دهد از طریق لنگر انداختن موارد را پیمایش کند.

یک دکمه پیوند کلیک می شود و در محتوای متمرکز می لغزد

برای مثال، کلیک کردن روی یک پیوند به طور خودکار مقاله :target در Chrome 89 متمرکز می کند، بدون نیاز به JS. سپس کاربر می تواند محتوای مقاله را با دستگاه ورودی خود مانند همیشه پیمایش کند. همانطور که در نشانه گذاری نشان داده شده است، محتوای مکمل است.

من از نشانه گذاری زیر برای سازماندهی برگه ها استفاده کردم:

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

سپس مقالات را با مقادیر ترکیبی لورم، و پیوندها را با طول و مجموعه تصویر ترکیبی از عناوین پر کردم. با محتوا برای کار، می توانیم طرح بندی را شروع کنیم.

طرح بندی های پیمایشی

3 نوع مختلف ناحیه اسکرول در این کامپوننت وجود دارد:

  • ناوبری (صورتی) به صورت افقی قابل پیمایش است
  • ناحیه محتوا (آبی) به صورت افقی قابل پیمایش است
  • هر مورد مقاله (سبز) به صورت عمودی قابل پیمایش است.
3 جعبه رنگارنگ با فلش‌های جهتی مطابق رنگ که قسمت‌های اسکرول را مشخص می‌کنند و جهت حرکت را نشان می‌دهند.

2 نوع عنصر مختلف در پیمایش وجود دارد:

  1. یک پنجره
    جعبه ای با ابعاد تعریف شده که دارای سبک ویژگی overflow است.
  2. یک سطح بزرگ
    در این طرح، این ظروف فهرست است: پیوندهای ناوبری، مقالات بخش، و محتوای مقاله.

طرح بندی <snap-tabs>

طرح سطح بالایی که من انتخاب کردم فلکس (Flexbox) بود. من جهت را روی column قرار دادم، بنابراین سرصفحه و بخش به صورت عمودی مرتب می شوند. این اولین پنجره اسکرول ما است و همه چیز را با سرریز پنهان پنهان می کند. سرصفحه و بخش به زودی از overscroll به عنوان مناطق جداگانه استفاده می کنند.

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 باید به صورت افقی همراه با گروه پیوندها حرکت کند و این طرح هدر به تنظیم آن مرحله کمک می کند. هیچ عنصری مطلقاً در اینجا وجود ندارد!

المان‌های nav و span.indicator روی آنها روکش‌هایی با رنگ داغ دارند که فضایی را که در مؤلفه اشغال می‌کنند مشخص می‌کند.

بعد، سبک های اسکرول. به نظر می رسد که ما می توانیم سبک های اسکرول را بین 2 ناحیه اسکرول افقی خود (سرصفحه و بخش) به اشتراک بگذاریم، بنابراین من یک کلاس کاربردی ساختم، .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>

پیوندهای پیمایش باید در یک خط، بدون شکستگی خط، به صورت عمودی در مرکز قرار گیرند، و هر مورد پیوند باید به محفظه اسکرول اسنپ بچسبد. کار سریع برای 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;
  }
}

هر پیوند سبک و اندازه خود را دارد، بنابراین طرح ناو فقط باید جهت و جریان را مشخص کند. عرض های منحصر به فرد در موارد ناوبری، انتقال بین برگه ها را سرگرم کننده می کند زیرا نشانگر عرض خود را با هدف جدید تنظیم می کند. بسته به تعداد عناصر موجود در اینجا، مرورگر یک اسکرول بار را نمایش می دهد یا خیر.

المان‌های a Nav دارای پوشش‌هایی با رنگ‌های داغ بر روی خود هستند که فضایی را که در جزء اشغال می‌کنند و همچنین محل سرریز شدن آنها را مشخص می‌کند.

طرح بندی برگه ها <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;
}

من تصمیم گرفتم که مقالات در اسکرول والد خود بچسبند. من واقعاً دوست دارم که چگونه آیتم‌های پیوند پیمایش و عناصر مقاله به شروع درونی محفظه‌های اسکرول مربوطه خود می‌چسبند. به نظر می رسد و احساس می کند که یک رابطه هماهنگ است.

عنصر مقاله و عناصر فرزند آن دارای روکش‌هایی به رنگ داغ روی خود هستند که فضایی را که در مؤلفه اشغال می‌کنند و جهتی که سرریز می‌کنند مشخص می‌کند.

این مقاله یک فرزند شبکه است و اندازه آن از پیش تعیین شده است که می‌خواهیم UX اسکرول را ارائه دهیم. این به این معنی است که من در اینجا به هیچ سبک ارتفاع یا عرض نیاز ندارم، فقط باید نحوه سرریز شدن آن را تعریف کنم. من overflow-y را روی خودکار تنظیم کردم و سپس تعاملات اسکرول را با ویژگی overscroll-behavior مفید به دام انداختم.

خلاصه 3 ناحیه اسکرول

در زیر من در تنظیمات سیستم خود "همیشه نوارهای پیمایش نشان داده شود" را انتخاب کرده ام. فکر می‌کنم کار با این تنظیم برای چیدمان دو چندان مهم است، زیرا برای من این است که طرح‌بندی و هماهنگ‌سازی اسکرول را بررسی کنم.

3 نوار پیمایش برای نشان دادن تنظیم شده‌اند، اکنون فضای طرح‌بندی را مصرف می‌کنند، و جزء ما همچنان عالی به نظر می‌رسد

من فکر می کنم دیدن ناودان اسکرول در این مؤلفه کمک می کند تا به وضوح نشان دهیم که مناطق اسکرول کجا هستند، جهتی که پشتیبانی می کنند و نحوه تعامل آنها با یکدیگر. در نظر بگیرید که چگونه هر یک از این قاب‌های پنجره پیمایشی برای یک چیدمان منعطف یا مشبک هستند.

DevTools می تواند به ما در تجسم این موضوع کمک کند:

نواحی اسکرول دارای پوشش‌های شبکه و ابزار فلکس‌باکس هستند که فضایی را که در جزء اشغال می‌کنند و جهتی که سرریز می‌کنند مشخص می‌کنند.
Chromium Devtools، طرح‌بندی عنصر flexbox nav را پر از عناصر لنگر، طرح‌بندی بخش شبکه پر از عناصر مقاله، و عناصر مقاله پر از پاراگراف و عنصر عنوان را نشان می‌دهد.

طرح‌بندی‌های اسکرول کامل هستند: snapping، عمیق قابل پیوند، و صفحه کلید قابل دسترسی. پایه ای قوی برای بهبود UX، سبک و لذت.

برجسته ویژگی

کودکان اسکرول شده موقعیت قفل خود را در طول تغییر اندازه حفظ می کنند. این بدان معناست که جاوا اسکریپت در چرخش دستگاه یا تغییر اندازه مرورگر نیازی به نمایش چیزی ندارد. آن را در حالت دستگاه Chromium DevTools با انتخاب هر حالتی غیر از Responsive و سپس تغییر اندازه قاب دستگاه امتحان کنید. توجه داشته باشید که عنصر در نمای باقی می ماند و با محتوای خود قفل می شود. از زمانی که Chromium اجرای خود را برای مطابقت با مشخصات به‌روزرسانی کرد، این مورد در دسترس است. در اینجا یک پست وبلاگ در مورد آن است.

انیمیشن

هدف از کار انیمیشن در اینجا این است که به وضوح تعاملات را با بازخورد UI پیوند دهد. این کمک می کند تا کاربر را در جهت کشف یکپارچه (امیدوارم) تمام محتوا راهنمایی یا کمک کند. من حرکت را با هدف و مشروط اضافه خواهم کرد. کاربران اکنون می توانند تنظیمات برگزیده حرکت خود را در سیستم عامل خود مشخص کنند و من کاملاً از پاسخ دادن به ترجیحات آنها در رابط های خود لذت می برم.

من یک زیر خط برگه را با موقعیت اسکرول مقاله پیوند خواهم داد. Snapping نه تنها یک تراز زیبا است، بلکه شروع و پایان یک انیمیشن را نیز ثابت می کند. این کار <nav> که مانند یک نقشه کوچک عمل می کند، به محتوا متصل نگه می دارد. ما اولویت حرکت کاربر را از CSS و JS بررسی خواهیم کرد. چند مکان عالی وجود دارد که باید مورد توجه قرار گیرد!

رفتار اسکرول

فرصتی برای بهبود رفتار حرکتی :target و element.scrollIntoView() وجود دارد. به طور پیش فرض، آنی است. مرورگر فقط موقعیت اسکرول را تنظیم می کند. خوب، اگر بخواهیم به جای پلک زدن در آنجا به آن موقعیت اسکرول منتقل شویم، چه؟

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

از آنجایی که ما در اینجا حرکت را معرفی می کنیم، و حرکتی را که کاربر کنترل نمی کند (مانند پیمایش)، فقط در صورتی این سبک را اعمال می کنیم که کاربر در سیستم عامل خود ترجیحی در مورد حرکت کاهش یافته نداشته باشد. به این ترتیب، ما فقط حرکت اسکرول را برای افرادی که با آن موافق هستند معرفی می کنیم.

نشانگر زبانه ها

هدف این انیمیشن کمک به مرتبط کردن شاخص با وضعیت محتوا است. من تصمیم گرفتم برای کاربرانی که حرکت کاهش‌یافته را ترجیح می‌دهند، سبک‌های متقاطع border-bottom را رنگ‌آمیزی کنم، و برای کاربرانی که با حرکت خوب هستند، یک انیمیشن اسلاید + محو رنگی پیوند خورده را رنگ آمیزی کنم.

در Chromium Devtools، می‌توانم اولویت را تغییر دهم و 2 سبک انتقال مختلف را نشان دهم. من از ساختن این بسیار لذت بردم.

@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 جایگزین می‌کنم. همچنین در تعامل برگه‌ها توجه کنید که آیتم navver فعال نه تنها دارای برجسته‌سازی زیر نام تجاری است، بلکه رنگ متن آن نیز تیره‌تر است. عنصر فعال دارای کنتراست رنگ متن بالاتر و لهجه زیر نور روشن است.

فقط چند خط اضافی از CSS باعث می شود کسی احساس کند دیده می شود (به این معنا که ما با دقت به اولویت های حرکتی آنها احترام می گذاریم). من آن را دوست دارم.

@scroll-timeline

در بخش بالا به شما نشان دادم که چگونه سبک‌های متقاطع حرکت کاهش‌یافته را مدیریت می‌کنم، و در این بخش به شما نشان می‌دهم که چگونه نشانگر و یک ناحیه اسکرول را به هم مرتبط کردم. این چند چیز تجربی سرگرم کننده در ادامه است. امیدوارم شما هم مثل من هیجان زده باشید.

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

من ابتدا اولویت حرکت کاربر را از جاوا اسکریپت بررسی می کنم. اگر نتیجه این false باشد، به این معنی که کاربر حرکت کاهش‌یافته را ترجیح می‌دهد، هیچ یک از افکت‌های حرکتی پیوند اسکرول را اجرا نمی‌کنیم.

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

در زمان نوشتن این مطلب، پشتیبانی مرورگر برای @scroll-timeline وجود ندارد. این یک پیش نویس مشخصات با پیاده سازی های آزمایشی است. این یک پلی فیل دارد که من در این نسخه نمایشی از آن استفاده می کنم.

ScrollTimeline

در حالی که CSS و جاوا اسکریپت می توانند هر دو جدول زمانی اسکرول ایجاد کنند، من جاوا اسکریپت را انتخاب کردم تا بتوانم از اندازه گیری عناصر زنده در انیمیشن استفاده کنم.

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 وجود ندارد، و هیچ راهی برای ایجاد پویا تعدادی فریم کلیدی بر اساس طول کودکان وجود ندارد.

با این حال، جاوا اسکریپت می داند که چگونه این اطلاعات را به دست آورد، بنابراین ما خودمان روی بچه ها تکرار می کنیم و مقادیر محاسبه شده را در زمان اجرا می گیریم:

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) پیوند را برجسته می کند و در غیر این صورت یک رنگ متن استاندارد است. حلقه تودرتو در آنجا آن را نسبتاً ساده می کند، زیرا حلقه بیرونی هر مورد ناوبری است و حلقه داخلی فریم های کلیدی شخصی هر یک از موارد ناوبری است. بررسی می‌کنم که آیا عنصر حلقه بیرونی مشابه حلقه داخلی است یا نه، و از آن برای اطلاع از انتخاب آن استفاده می‌کنم.

از نوشتن این مطلب خیلی لذت بردم. خیلی زیاد.

حتی پیشرفت های بیشتر جاوا اسکریپت

شایان ذکر است که هسته چیزی که در اینجا به شما نشان می دهم بدون جاوا اسکریپت کار می کند. با این اوصاف، بیایید ببینیم چگونه می‌توانیم آن را هنگامی که 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);
};

با فرض قطع شدن اسکرول، تقسیم موقعیت اسکرول فعلی از عرض ناحیه اسکرول باید به یک عدد صحیح و نه اعشاری منجر شود. سپس سعی می‌کنم از طریق این شاخص محاسبه‌شده، یک navitem از حافظه پنهان خود بگیرم و اگر چیزی پیدا کرد، مسابقه را برای فعال شدن ارسال می‌کنم.

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 را اعمال می کند. جاوا اسکریپت می‌تواند آزادانه برای اسکرول عناصر تماس برقرار کند، و CSS می‌تواند UX را به‌طور آشکار مدیریت کند. کبریت کوچک بسیار لذت بخشی که گاهی اوقات درست می کنند.

نتیجه گیری

حالا که می دانی من چگونه این کار را انجام دادم، شما چطور؟! این باعث می شود معماری اجزای سرگرم کننده ای ایجاد شود! چه کسی می خواهد نسخه اول را با اسلات در چارچوب مورد علاقه خود بسازد؟ 🙂

بیایید رویکردهایمان را متنوع کنیم و همه راه‌های ساخت در وب را بیاموزیم. یک اشکال ایجاد کنید، نسخه خود را برای من توییت کنید ، و من آن را به بخش ریمیکس های انجمن در زیر اضافه می کنم.

ریمیکس های انجمن

،

یک نمای کلی از نحوه ساخت یک مؤلفه برگه ها مشابه آنچه در برنامه های iOS و Android یافت می شود.

در این پست می‌خواهم تفکری را در مورد ساخت یک جزء Tabs برای وب به اشتراک بگذارم که واکنش‌گرا باشد، از ورودی‌های چندگانه دستگاه پشتیبانی کند و در مرورگرها کار کند. نسخه ی نمایشی را امتحان کنید.

نسخه ی نمایشی

اگر ویدیو را ترجیح می دهید، در اینجا یک نسخه YouTube از این پست وجود دارد:

نمای کلی

زبانه ها جزء رایج سیستم های طراحی هستند اما می توانند اشکال و اشکال مختلفی داشته باشند. ابتدا برگه‌های دسکتاپ بر روی عنصر <frame> ساخته شده بودند، و اکنون مؤلفه‌های موبایل کره‌ای داریم که محتوا را بر اساس ویژگی‌های فیزیکی متحرک می‌کنند. همه آنها تلاش می کنند یک کار را انجام دهند: صرفه جویی در فضا.

امروزه، ملزومات تجربه کاربر تب ها، یک ناحیه پیمایش دکمه ای است که نمایان بودن محتوا را در یک قاب نمایشگر تغییر می دهد. بسیاری از حوزه‌های محتوای مختلف فضای یکسانی را به اشتراک می‌گذارند، اما به صورت مشروط بر اساس دکمه انتخاب‌شده در پیمایش ارائه می‌شوند.

کلاژ به دلیل تنوع زیاد سبک هایی که وب در مفهوم کامپوننت اعمال کرده است کاملاً آشفته است
مجموعه ای از سبک های طراحی وب کامپوننت برگه در 10 سال گذشته

تاکتیک های وب

در مجموع به لطف چند ویژگی مهم پلتفرم وب، ساخت این مؤلفه را بسیار ساده دیدم:

  • scroll-snap-points برای تعاملات زیبا با کشیدن انگشت و صفحه کلید با موقعیت های توقف پیمایش مناسب
  • پیوندهای عمیق از طریق هش‌های URL برای پشتیبانی از لنگر انداختن پیمایش درون صفحه و به اشتراک‌گذاری توسط مرورگر
  • پشتیبانی از صفحه‌خوان با نشانه‌گذاری عنصر <a> و id="#hash"
  • prefers-reduced-motion برای فعال کردن انتقال متقاطع و پیمایش فوری در صفحه
  • ویژگی وب پیش نویس @scroll-timeline برای خط کشی پویا و تغییر رنگ برگه انتخاب شده

HTML

اساساً، UX در اینجا این است: روی یک پیوند کلیک کنید، نشانی اینترنتی نشان دهنده وضعیت صفحه تو در تو باشد، و سپس به‌روزرسانی منطقه محتوا را در حین حرکت مرورگر به عنصر منطبق ببینید.

برخی از اعضای محتوای ساختاری در آن وجود دارد: پیوندها و :target s. ما به لیستی از پیوندها نیاز داریم که یک <nav> برای آنها عالی است، و یک لیست از عناصر <article> که یک <section> برای آنها عالی است. هر هش پیوند با یک بخش مطابقت دارد و به مرورگر اجازه می‌دهد از طریق لنگر انداختن موارد را پیمایش کند.

یک دکمه پیوند کلیک می شود و در محتوای متمرکز می لغزد

برای مثال، کلیک کردن روی یک پیوند به طور خودکار مقاله :target در Chrome 89 متمرکز می کند، بدون نیاز به JS. سپس کاربر می تواند محتوای مقاله را با دستگاه ورودی خود مانند همیشه پیمایش کند. همانطور که در نشانه گذاری نشان داده شده است، محتوای مکمل است.

من از نشانه گذاری زیر برای سازماندهی برگه ها استفاده کردم:

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

سپس مقالات را با مقادیر ترکیبی لورم، و پیوندها را با طول و مجموعه تصویر ترکیبی از عناوین پر کردم. با محتوا برای کار، می توانیم طرح بندی را شروع کنیم.

طرح بندی های پیمایشی

3 نوع مختلف ناحیه اسکرول در این کامپوننت وجود دارد:

  • ناوبری (صورتی) به صورت افقی قابل پیمایش است
  • ناحیه محتوا (آبی) به صورت افقی قابل پیمایش است
  • هر مورد مقاله (سبز) به صورت عمودی قابل پیمایش است.
3 جعبه رنگارنگ با فلش‌های جهتی مطابق رنگ که قسمت‌های اسکرول را مشخص می‌کنند و جهت حرکت را نشان می‌دهند.

2 نوع عنصر مختلف در پیمایش وجود دارد:

  1. یک پنجره
    جعبه ای با ابعاد تعریف شده که دارای سبک ویژگی overflow است.
  2. یک سطح بزرگ
    در این طرح، این ظروف فهرست است: پیوندهای ناوبری، مقالات بخش، و محتوای مقاله.

طرح بندی <snap-tabs>

طرح سطح بالایی که من انتخاب کردم فلکس (Flexbox) بود. من جهت را روی column قرار دادم، بنابراین سرصفحه و بخش به صورت عمودی مرتب می شوند. این اولین پنجره اسکرول ما است و همه چیز را با سرریز پنهان پنهان می کند. سرصفحه و بخش به زودی از overscroll به عنوان مناطق جداگانه استفاده می کنند.

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 باید به صورت افقی همراه با گروه پیوندها حرکت کند و این طرح هدر به تنظیم آن مرحله کمک می کند. هیچ عنصری مطلقاً در اینجا وجود ندارد!

المان‌های nav و span.indicator روی آنها روکش‌هایی با رنگ داغ دارند که فضایی را که در مؤلفه اشغال می‌کنند مشخص می‌کند.

بعد، سبک های اسکرول. به نظر می رسد که ما می توانیم سبک های اسکرول را بین 2 ناحیه اسکرول افقی خود (سرصفحه و بخش) به اشتراک بگذاریم، بنابراین من یک کلاس کاربردی ساختم، .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>

پیوندهای پیمایش باید در یک خط، بدون شکستگی خط، به صورت عمودی در مرکز قرار گیرند، و هر مورد پیوند باید به محفظه اسکرول اسنپ بچسبد. کار سریع برای 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;
  }
}

هر پیوند سبک و اندازه خود را دارد، بنابراین طرح ناو فقط باید جهت و جریان را مشخص کند. عرض های منحصر به فرد در موارد ناوبری، انتقال بین برگه ها را سرگرم کننده می کند زیرا نشانگر عرض خود را با هدف جدید تنظیم می کند. بسته به تعداد عناصر موجود در اینجا، مرورگر یک اسکرول بار را نمایش می دهد یا خیر.

المان‌های a Nav دارای پوشش‌هایی با رنگ‌های داغ بر روی خود هستند که فضایی را که در جزء اشغال می‌کنند و همچنین محل سرریز شدن آنها را مشخص می‌کند.

طرح بندی برگه ها <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;
}

من تصمیم گرفتم که مقالات در اسکرول والد خود بچسبند. من واقعاً دوست دارم که چگونه آیتم‌های پیوند پیمایش و عناصر مقاله به شروع درونی محفظه‌های اسکرول مربوطه خود می‌چسبند. به نظر می رسد و احساس می کند که یک رابطه هماهنگ است.

عنصر مقاله و عناصر فرزند آن دارای روکش‌هایی به رنگ داغ روی خود هستند که فضایی را که در مؤلفه اشغال می‌کنند و جهتی که سرریز می‌کنند مشخص می‌کند.

این مقاله یک فرزند شبکه است و اندازه آن از پیش تعیین شده است که می‌خواهیم UX اسکرول را ارائه دهیم. این به این معنی است که من در اینجا به هیچ سبک ارتفاع یا عرض نیاز ندارم، فقط باید نحوه سرریز شدن آن را تعریف کنم. من overflow-y را روی خودکار تنظیم کردم و سپس تعاملات اسکرول را با ویژگی overscroll-behavior مفید به دام انداختم.

خلاصه 3 ناحیه اسکرول

در زیر من در تنظیمات سیستم خود "همیشه نوارهای پیمایش نشان داده شود" را انتخاب کرده ام. فکر می‌کنم کار با این تنظیم برای چیدمان دو چندان مهم است، زیرا برای من این است که طرح‌بندی و هماهنگ‌سازی اسکرول را بررسی کنم.

3 نوار پیمایش برای نشان دادن تنظیم شده‌اند، اکنون فضای طرح‌بندی را مصرف می‌کنند، و جزء ما همچنان عالی به نظر می‌رسد

من فکر می کنم دیدن ناودان اسکرول در این مؤلفه کمک می کند تا به وضوح نشان دهیم که مناطق اسکرول کجا هستند، جهتی که پشتیبانی می کنند و نحوه تعامل آنها با یکدیگر. در نظر بگیرید که چگونه هر یک از این قاب‌های پنجره پیمایشی برای یک چیدمان منعطف یا مشبک هستند.

DevTools می تواند به ما در تجسم این موضوع کمک کند:

نواحی اسکرول دارای پوشش‌های شبکه و ابزار فلکس‌باکس هستند که فضایی را که در جزء اشغال می‌کنند و جهتی که سرریز می‌کنند مشخص می‌کنند.
Chromium Devtools، طرح‌بندی عنصر flexbox nav را پر از عناصر لنگر، طرح‌بندی بخش شبکه پر از عناصر مقاله، و عناصر مقاله پر از پاراگراف و عنصر عنوان را نشان می‌دهد.

طرح‌بندی‌های اسکرول کامل هستند: snapping، عمیق قابل پیوند، و صفحه کلید قابل دسترسی. پایه ای قوی برای بهبود UX، سبک و لذت.

برجسته ویژگی

کودکان اسکرول شده موقعیت قفل خود را در طول تغییر اندازه حفظ می کنند. این بدان معناست که جاوا اسکریپت در چرخش دستگاه یا تغییر اندازه مرورگر نیازی به نمایش چیزی ندارد. آن را در حالت دستگاه Chromium DevTools با انتخاب هر حالتی غیر از Responsive و سپس تغییر اندازه قاب دستگاه امتحان کنید. توجه داشته باشید که عنصر در نمای باقی می ماند و با محتوای خود قفل می شود. از زمانی که Chromium اجرای خود را برای مطابقت با مشخصات به‌روزرسانی کرد، این مورد در دسترس است. در اینجا یک پست وبلاگ در مورد آن است.

انیمیشن

هدف از کار انیمیشن در اینجا این است که به وضوح تعاملات را با بازخورد UI پیوند دهد. این کمک می کند تا کاربر را در جهت کشف یکپارچه (امیدوارم) تمام محتوا راهنمایی یا کمک کند. من حرکت را با هدف و مشروط اضافه خواهم کرد. کاربران اکنون می توانند تنظیمات برگزیده حرکت خود را در سیستم عامل خود مشخص کنند و من کاملاً از پاسخ دادن به ترجیحات آنها در رابط های خود لذت می برم.

من یک زیر خط برگه را با موقعیت اسکرول مقاله پیوند خواهم داد. Snapping نه تنها یک تراز زیبا است، بلکه شروع و پایان یک انیمیشن را نیز ثابت می کند. این کار <nav> که مانند یک نقشه کوچک عمل می کند، به محتوا متصل نگه می دارد. ما اولویت حرکت کاربر را از CSS و JS بررسی خواهیم کرد. چند مکان عالی وجود دارد که باید مورد توجه قرار گیرد!

رفتار اسکرول

فرصتی برای بهبود رفتار حرکتی :target و element.scrollIntoView() وجود دارد. به طور پیش فرض، آنی است. مرورگر فقط موقعیت اسکرول را تنظیم می کند. خوب، اگر بخواهیم به جای پلک زدن در آنجا به آن موقعیت اسکرول منتقل شویم، چه؟

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

از آنجایی که ما در اینجا حرکت را معرفی می کنیم، و حرکتی را که کاربر کنترل نمی کند (مانند پیمایش)، فقط در صورتی این سبک را اعمال می کنیم که کاربر در سیستم عامل خود ترجیحی در مورد حرکت کاهش یافته نداشته باشد. به این ترتیب، ما فقط حرکت اسکرول را برای افرادی که با آن موافق هستند معرفی می کنیم.

نشانگر زبانه ها

هدف این انیمیشن کمک به مرتبط کردن شاخص با وضعیت محتوا است. من تصمیم گرفتم برای کاربرانی که حرکت کاهش‌یافته را ترجیح می‌دهند، سبک‌های متقاطع border-bottom را رنگ‌آمیزی کنم، و برای کاربرانی که با حرکت خوب هستند، یک انیمیشن اسلاید + محو رنگی پیوند خورده را رنگ آمیزی کنم.

در Chromium Devtools، می‌توانم اولویت را تغییر دهم و 2 سبک انتقال مختلف را نشان دهم. من از ساختن این بسیار لذت بردم.

@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 جایگزین می‌کنم. همچنین در تعامل برگه‌ها توجه کنید که آیتم navver فعال نه تنها دارای برجسته‌سازی زیر نام تجاری است، بلکه رنگ متن آن نیز تیره‌تر است. عنصر فعال دارای کنتراست رنگ متن بالاتر و لهجه زیر نور روشن است.

فقط چند خط اضافی از CSS باعث می شود کسی احساس کند دیده می شود (به این معنا که ما با دقت به اولویت های حرکتی آنها احترام می گذاریم). من آن را دوست دارم.

@scroll-timeline

در بخش بالا به شما نشان دادم که چگونه سبک‌های متقاطع حرکت کاهش‌یافته را مدیریت می‌کنم، و در این بخش به شما نشان می‌دهم که چگونه نشانگر و یک ناحیه اسکرول را به هم مرتبط کردم. این چند چیز تجربی سرگرم کننده در ادامه است. امیدوارم شما هم مثل من هیجان زده باشید.

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

من ابتدا اولویت حرکت کاربر را از جاوا اسکریپت بررسی می کنم. اگر نتیجه این false باشد، به این معنی که کاربر حرکت کاهش‌یافته را ترجیح می‌دهد، هیچ یک از افکت‌های حرکتی پیوند اسکرول را اجرا نمی‌کنیم.

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

در زمان نوشتن این مطلب، پشتیبانی مرورگر برای @scroll-timeline وجود ندارد. این یک پیش نویس مشخصات با پیاده سازی های آزمایشی است. این یک پلی فیل دارد که من در این نسخه نمایشی از آن استفاده می کنم.

ScrollTimeline

در حالی که CSS و جاوا اسکریپت می توانند هر دو جدول زمانی اسکرول ایجاد کنند، من جاوا اسکریپت را انتخاب کردم تا بتوانم از اندازه گیری عناصر زنده در انیمیشن استفاده کنم.

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 کلید اصلی برای انیمیشن می شود. همین کار برای عرض انجام می شود ، از هرکدام سؤال می شود که عرض پویا آن چیست و سپس از آن به عنوان یک مقدار کلید استفاده می شود.

در اینجا به عنوان مثال ، بر اساس قلم های من و تنظیمات مرورگر من وجود دارد:

Keyframes 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 نشان داده شده است که نمرات کنتراست عبور را برای هر دو نشان می دهد

کاربر با تعامل خود انیمیشن را هدایت می کند ، با دیدن عرض و موقعیت تغییر شاخص از یک بخش به بخش دیگر ، پیگیری کاملاً با پیمایش.

شاید شما متوجه نشده باشید ، اما من از انتقال رنگ بسیار افتخار می کنم زیرا مورد ناوبری برجسته انتخاب می شود.

خاکستری سبک تر انتخاب نشده حتی بیشتر به عقب رانده می شود وقتی که مورد برجسته کنتراست بیشتری دارد. معمول است که رنگ انتقال برای متن ، مانند Hover و در هنگام انتخاب ، اما انتقال آن رنگ در پیمایش ، همگام سازی با نشانگر زیر خط ، سطح بعدی است.

در اینجا نحوه انجام من این است:

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

هر پیوند NAV Tab به این انیمیشن رنگی جدید نیاز دارد و همان جدول زمانی پیمایش را به عنوان نشانگر زیر خط ردیابی می کند. من از همان جدول زمانی مانند گذشته استفاده می کنم: از آنجا که نقش این است که یک کنه را در پیمایش منتشر کنیم ، می توانیم از آن تیک در هر نوع انیمیشن مورد نظر استفاده کنیم. همانطور که قبلاً انجام دادم ، من 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)",
]

Keyframe با رنگ var(--text-active-color) لینک را برجسته می کند و در غیر این صورت یک رنگ متنی استاندارد است. حلقه تو در تو در آنجا آن را نسبتاً ساده می کند ، زیرا حلقه بیرونی هر مورد NAV است و حلقه داخلی هر یک از کلید های شخصی Navitem است. من بررسی می کنم که آیا عنصر حلقه بیرونی همان حلقه داخلی است و از آن استفاده می کند تا در هنگام انتخاب آن بدانید.

از نوشتن این مطلب خیلی لذت بردم. خیلی زیاد.

حتی بیشتر پیشرفتهای جاوا اسکریپت

ارزش یادآوری دارد که هسته اصلی آنچه من در اینجا به شما نشان می دهم بدون 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); });

هر زمان که بخش ها در حال پیمایش هستند ، در صورت وجود بخش زمان را پاک کنید و یک بخش جدید را شروع کنید. هنگامی که بخش ها از پیمایش متوقف می شوند ، زمان را پاک نکنید و پس از استراحت 100ms آتش بزنید. هنگام آتش سوزی ، عملکردی را که می خواهد بفهمد کجا کاربر متوقف شده است ، تماس بگیرید.

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

  matchingNavItem && setActiveTab(matchingNavItem);
};

با فرض اینکه پیمایش می شود ، تقسیم موقعیت کتیبه فعلی از عرض ناحیه پیمایش باید به یک عدد صحیح منجر شود و نه اعشاری. سپس سعی می کنم از طریق این شاخص محاسبه شده ، یک Navitem را از حافظه نهان خود بگیرم و اگر چیزی پیدا کند ، مسابقه را برای فعال کردن ارسال می کنم.

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

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

تنظیم برگه فعال با پاک کردن هر برگه فعال در حال حاضر شروع می شود و سپس به مورد NAV ورودی ویژگی حالت فعال می دهد. تماس با 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;
  }
}

در برنامه Snap Snap Snap CSS ، ما یک پرس و جو رسانه ای را در نظر گرفته ایم که در صورت تحمل حرکت کاربر ، پیمایش smooth را اعمال می کند. JavaScript می تواند آزادانه تماس با عناصر را به سمت مشاهده انجام دهد ، و CSS می تواند UX را به صورت اعلامیه مدیریت کند. کاملاً دلپذیر و دلپذیر که بعضی اوقات انجام می دهند.

نتیجه گیری

حالا که می دانید من چگونه این کار را کردم ، چطور؟! این باعث می شود معماری مؤلفه سرگرم کننده باشد! چه کسی قصد دارد نسخه اول را با اسلات در چارچوب مورد علاقه خود بسازد؟ 🙂

بیایید رویکردهای خود را متنوع کنیم و تمام راه های ساخت در وب را بیاموزیم. یک درخشش ایجاد کنید ، نسخه خود را برای من توییت کنید و من آن را به بخش Remixes Community در زیر اضافه می کنم.

ریمیکس های جامعه