Tworzenie komponentu Karty

Podstawowe informacje o tworzeniu komponentu kart podobnego do tych, które można znaleźć w aplikacjach na iOS i Androida.

W tym poście pokażę, jak stworzyć komponent kart, który jest elastyczny, obsługuje wiele urządzeń wejściowych i działa w różnych przeglądarkach. Zobacz prezentację.

Demonstracja

Jeśli wolisz film, oto wersja tego posta na YouTube:

Przegląd

Karty są częstym elementem systemów projektowania, ale mogą przybierać różne kształty. Najpierw były karty na komputery zbudowane na elemencie <frame>, a teraz istnieją bardziej precyzyjne komponenty na urządzenia mobilne, które animują treści na podstawie właściwości fizycznych. Wszyscy chcą robić to samo: oszczędzać miejsce.

Obecnie najważniejszym elementem obsługi kart jest obszar nawigacyjny przy użyciu przycisków, który pozwala przełączać widoczność treści w ramce wyświetlanej. Różne obszary treści znajdują się w tym samym obszarze, ale są wyświetlane warunkowo w zależności od przycisku wybranego w menu nawigacyjnym.

kolaż jest dość chaotyczny ze względu na ogromną różnorodność stylów stosowanych w internecie do koncepcji komponentów
Kolaż stylów projektu strony internetowej z ostatnich 10 lat

Taktyki internetowe

Stworzenie tego komponentu było proste dzięki kilku kluczowym funkcjom platformy internetowej:

  • scroll-snap-points do eleganckiego przesuwania i używania klawiatury z odpowiednimi pozycjami zatrzymania przewijania
  • Precyzyjne linki (za pomocą haszów adresów URL) na potrzeby obsługi kotwiczenia i udostępniania przewijania na stronie w przeglądarce
  • Obsługa czytnika ekranu ze znacznikami elementów <a> i id="#hash".
  • prefers-reduced-motion – umożliwia przejście w tle i błyskawiczne przewijanie na stronie
  • Funkcja internetowa @scroll-timeline w wersji roboczej do dynamicznego podkreślania i zmieniania kolorów wybranej karty

Kod HTML

Zasadniczo interfejs wygląda tak: kliknij link, ustaw adres URL jako zagnieżdżoną stronę, a następnie sprawdź, czy obszar treści jest aktualizowany, gdy przeglądarka przewija stronę do pasującego elementu.

Są w nich treści strukturalne: linki i elementy :target. Potrzebujemy listy linków, w przypadku których warto użyć atrybutu <nav>, oraz listy elementów <article>, w przypadku których warto użyć atrybutu <section>. Każdy hasz linku pasuje do sekcji, dzięki czemu przeglądarka przewija elementy za pomocą kotwicy.

Następuje kliknięcie przycisku linku i wysuwanie się po zaznaczeniu treści

Na przykład kliknięcie linku automatycznie powoduje wyróżnienie artykułu :target w Chrome 89, bez konieczności użycia kodu JS. Użytkownik może jak zawsze przewinąć treść artykułu za pomocą urządzenia wejściowego. Są to treści bezpłatne, wskazane w znacznikach.

Do uporządkowania kart zostały użyte następujące znaczniki:

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

Mogę tworzyć połączenia między elementami <a> i <article> za pomocą właściwości href i id w ten sposób:

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

Następnie zamieściłam artykuły w różnych ilościach lorem, a linki – o różnej długości i z zestawem obrazów. Mając treści do popracowania, możemy zacząć układ.

Układy przewijane

W tym komponencie dostępne są 3 różne rodzaje obszarów przewijania:

  • Nawigacja (różowy) można przewijać w poziomie
  • Obszar treści (niebieski) można przewijać w poziomie
  • Każdy element artykułu (zielona) można przewijać w pionie.
3 kolorowe prostokąty ze strzałkami kierunkowymi, które określają kierunek przewijania i wskazują kierunek przewijania.

Przewijanie obejmuje 2 różne rodzaje elementów:

  1. Okno
    Pole ze zdefiniowanymi wymiarami w stylu właściwości overflow.
  2. Zbyt duża powierzchnia
    W tym układzie są to kontenery listy: linki nawigacyjne, artykuły do sekcji i zawartość artykułów.

Układ <snap-tabs>

Wybrany przeze mnie układ najwyższego poziomu był elastyczny (Flexbox). Ustawiam kierunek na column, więc nagłówek i sekcja są ułożone pionowo. To nasze pierwsze okno przewijania, w którym wszystko jest ukryte. W nagłówku i sekcji jako poszczególne strefy wkrótce zostanie zastosowana nadmierna przewijanie.

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

Zwracamy uwagę na kolorowy diagram 3 przewijania:

  • <header> może być kontenerem przewijania (różowy).
  • <section> może być kontenerem przewijania (niebieski).

Ramki wyróżnione poniżej za pomocą VisBug pozwalają zobaczyć okna utworzone przez kontenery przewijania.

elementy nagłówka i sekcji mają nakładki hotpink, które określają miejsce zajmowane przez komponent.

Układ <header> kart

Następny układ jest prawie taki sam: do tworzenia kolejności pionowej używam funkcji Flex.

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

Element .snap-indicator powinien przemieszczać się w poziomie wraz z grupą linków. Ten układ nagłówka ułatwia ustawienie tego etapu. Nie ma tu żadnych elementów o pozycji bezwzględnej.

elementy nawigacyjne i span.indicator mają nakładki hotpink, które określają miejsce zajmowane w komponencie.

Następnie wybierz style przewijania. Okazuje się, że można stosować style przewijania w 2 poziomych obszarach przewijania (nagłówek i sekcja), więc stworzyłem klasę narzędziową o nazwie .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;
    }
  }
}

Każdy element musi mieć przepełnienie na osi X, i funkcję przewijania w celu zawijania przewijania, ukryte paski przewijania w przypadku urządzeń dotykowych oraz pasek przewijania w celu zablokowania obszarów prezentacji treści. Kolejność kart klawiatury jest dostępna, a wszystkie interakcje prowadzą do niej w naturalny sposób. Kontenery przewijane będą też otrzymywać ładną interakcję w stylu karuzeli z klawiaturą.

Układ nagłówka kart <nav>

Linki nawigacyjne muszą być ułożone w kolejności bez podziałów wiersza, wyśrodkowane w pionie, a każdy element linku powinien przyciągać do kontenera przewijania. Świetnie Ci idzie w ramach usługi porównywania cen na rok 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;
  }
}

Wszystkie style i rozmiary linków, więc układ nawigacji wymaga tylko określenia kierunku i przepływu. Unikalne szerokości elementów nawigacyjnych sprawiają, że przejście między kartami jest przyjemne, ponieważ wskaźnik dopasowuje swoją szerokość do nowego celu. W zależności od liczby elementów przeglądarka wyświetli pasek przewijania.

elementy nawigacji mają nakładki hotpink, które określają miejsce zajmowane przez element, a także gdzie się przestają;

Układ <section> kart

Ta sekcja jest elementem elastycznym i musi być głównym konsumentem przestrzeni. Musi też utworzyć kolumny, w których zostaną umieszczone artykuły. Jeszcze raz przypomnij sobie szybką pracę na sezonie CSS 2021. block-size: 100% rozciąga ten element tak, aby jak najlepiej wypełnić element nadrzędny. Następnie na potrzeby własnego układu tworzy szereg kolumn o szerokości 100% szerokości elementu nadrzędnego. Procenty sprawdzają się w tym przypadku, bo zawierają wyraźne ograniczenia dotyczące elementu nadrzędnego.

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

To tak, jakby mówić: „jak największego rozmiaru to możliwe w pionie” (pamiętaj, że nagłówek ustawiony na flex-shrink: 0 to zabezpieczenie przed rozwinięciem), który ustawia wysokość wiersza dla zestawu kolumn o pełnej wysokości. Styl auto-flow informuje siatkę, aby elementy podrzędne zawsze były układane w poziomej linii, bez zawijania tekstu, zgodnie z oczekiwaniami – poza oknem nadrzędnym.

elementy artykułu mają nakładki hotpink, które określają miejsce zajmowane przez element i miejsce, w którym się przenoszą.

Czasami ciężko mi się zapanować nad głową. Ten element sekcji pasuje do pudełka, ale tworzy też zestaw pudełek. Mam nadzieję, że efekty wizualne i wyjaśnienia okażą się przydatne.

Układ <article> kart

Użytkownik powinien mieć możliwość przewijania treści artykułu, a paski przewijania powinny być widoczne tylko wtedy, gdy strona jest przepełniona. Te elementy artykułu są ułożone w odpowiednich miejscach. Stanowią jednocześnie element nadrzędny przewijania i element podrzędny przewijanej. Przeglądarka poradzi sobie z niektórymi interakcjami z dotykiem, myszą i klawiaturą.

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

Artykuły mają być przyciągane do ramki przewijania nadrzędnego. Podoba mi się sposób przyciągania elementów linków nawigacyjnych i artykułów do elementu wbudowanego na początku odpowiednich kontenerów przewijania. Wygląda to na harmonijną relację.

element artykułu i jego elementy podrzędne mają nakładki Hotpink, które określają miejsce zajmowane w komponencie oraz kierunek, w którym się przestają;

Artykuł jest elementem podrzędnym siatki, a jego rozmiar jest wstępnie określony jako obszar widocznego obszaru, który chcemy zapewnić UX przewijania. To znaczy, że nie potrzebuję żadnych stylów wysokości ani szerokości. Ustawiam overflow-y na „auto”, a potem dodatkowo ukrywam interakcje przewijania za pomocą przydatnej właściwości Overscrolling.

Podsumowanie 3 obszarów przewijania

Poniżej wybrana przeze mnie w ustawieniach systemu opcja „Zawsze pokazuj paski przewijania”. Uważam, że układ i administracja przewijaniem są podwójnie ważne, gdy włączone jest to ustawienie.

3 paski przewijania są ustawione tak, aby się wyświetlały, zajmuje to miejsce w układzie, więc nasz komponent nadal wygląda świetnie.

Wydaje mi się, że w tym komponencie widać margines z paskiem przewijania, co pomaga w wyraźny sposób umiejscowić obszary przewijania, obsługiwany kierunek i sposób ich interakcji z innymi obszarami. Zastanów się, czy każda z ramek okna przewijania jest elastycznym lub nadrzędnym układem elementu.

Dzięki Narzędziom deweloperskim możemy to sprawdzić:

obszary przewijania zawierają nakładki siatki i narzędzi Flexbox, które wskazują miejsce zajmowane w komponencie oraz kierunek, w którym się pojawiają;
Narzędzia deweloperskie Chromium przedstawiające układ elementów nawigacyjnych flexbox z elementami zakotwiczonymi, układ sekcji siatki z elementami artykułu, elementy artykułu pełne akapitów i element nagłówka.

Układy przewijania są gotowe – przyciąganie, precyzyjne linki i dostęp za pomocą klawiatury. To solidna podstawa do wprowadzania ulepszeń w UX, stylu i przyjemności.

Wyróżnione funkcje

Przewijane dzieci zachowują swoją pozycję podczas zmiany rozmiaru. Oznacza to, że JavaScript nie musi wyświetlać żadnych elementów przy obracaniu urządzenia ani zmianie rozmiaru przeglądarki. Wypróbuj tę funkcję w trybie urządzenia w Narzędziach deweloperskich w Chromium, wybierając dowolny tryb inny niż Elastyczny, a następnie zmieniając rozmiar ramki urządzenia. Zwróć uwagę, że element pozostaje widoczny i zablokowany wraz z zawartością. Stało się tak, ponieważ Chromium zaktualizował implementację, aby była zgodna ze specyfikacją. Oto post na blogu na ten temat.

Animacja

Celem animacji jest wyraźne powiązanie interakcji z opiniami interfejsu. Pomaga to (mam nadzieję) poprowadzić użytkownika do łatwego odkrywania wszystkich treści. Będę dodawać ruch z celem i warunkowo. Użytkownicy mogą teraz określać ustawienia ruchu w swoich systemach operacyjnych, a ja doskonale odpowiadam na ich preferencje w moich interfejsach.

Podam podkreślenie karty z informacjami o miejscu przewijania artykułu. Przyciąganie to nie tylko wyrównanie, ale także zakotwiczenie początku i końca animacji. Dzięki temu <nav>, który działa jak minimapa, jest połączony z treścią. Będziemy sprawdzać preferencje użytkownika dotyczące ruchu zarówno przez CSS, jak i JS. Jest kilka miejsc, w których warto się zastanowić.

Zachowanie podczas przewijania

Istnieje możliwość ulepszenia funkcji związanych z ruchem zarówno w przypadku elementów :target, jak i element.scrollIntoView(). Domyślnie jest to błyskawiczne. Przeglądarka określa pozycję przewijania. A co, jeśli zamiast tam mrugać, można przejść do tej pozycji?

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

Wprowadzamy tu ruch, a ruch, którego użytkownik nie kontroluje (np. przewijanie), stosujemy tylko wtedy, gdy użytkownik nie ma preferencji dotyczących zmniejszonego ruchu w systemie operacyjnym. W ten sposób udostępniamy ruch przewijania tylko tym osobom, którym się to nie spodoba.

Wskaźnik kart

Ta animacja ma pomóc powiązać wskaźnik ze stanem treści. Zdecydowałam się na przenikanie kolorów w stylach border-bottom dla użytkowników, którzy wolą ograniczony ruch, a dla użytkowników, którym nie przeszkadza ruch, oraz przewijanie połączone przesuwaniem i zanikanie kolorów.

W narzędziach deweloperskich w Chromium mogę przełączać ustawienie i zademonstrować 2 różne style przejścia. Świetnie mi się to podobało.

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

Ukryję .snap-indicator, gdy użytkownik woli mniejszy ruch, bo już go nie potrzebuję. Następnie zastąpię go stylami border-block-end i transition. Zwróć też uwagę na to, że aktywny element nawigacji ma nie tylko podkreślenie marki, ale też kolor tekstu jest ciemniejszy. Element aktywny ma wyższy kontrast koloru tekstu i jasny akcent podświetlenia.

Już kilka dodatkowych wierszy CSS sprawi, że odbiorcy poczują się zauważeni (czyli z przemyślaniem szanujemy ich preferencje dotyczące ruchu). Uwielbiam to.

@scroll-timeline

W sekcji powyżej pokazałem, jak radzić sobie ze stylami zmniejszonego przenikania ruchu, a w tej sekcji – jak połączyć wskaźnik z obszarem przewijania. Za chwilę pojawią się nowe, eksperymentalne funkcje. Mam nadzieję, że tak jak ja.

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

Najpierw sprawdzam ustawienie ruchu użytkownika w JavaScript. Jeśli wynikiem tej operacji jest false, czyli użytkownik woli mniejszy ruch, nie uruchomimy żadnych efektów związanych z przewijaniem.

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

Obecnie nie obsługujemy przeglądarki @scroll-timeline. To wersja robocza specyfikacji zawierająca wyłącznie wdrożenia eksperymentalne. Jest tam jednak kod polyfill, który wykorzystam w tej prezentacji.

ScrollTimeline

CSS i JavaScript mogą tworzyć zarówno oś czasu przewijania, jak i język JavaScript, który pozwalał na pomiar mierzonych elementów w animacji.

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

Chcę, aby 1 rzeczy następowała po pozycji przewijania innego użytkownika, a tworząc element ScrollTimeline, zdefiniuję sterownik linku przewijania – scrollSource. Zwykle animacja w internecie jest wyświetlana zgodnie z globalnym przedziałem czasu, ale dzięki niestandardowemu ustawieniu sectionScrollTimeline w pamięci mogę to wszystko zmienić.

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

Zanim przejdę do klatek kluczowych animacji, muszę wskazać obserwatora przewijania (tabindicator), który będzie animowany na podstawie niestandardowej osi czasu, czyli przewijania sekcji. Powoduje to zakończenie połączenia, ale brak jest końcowego składnika, punktów stanowych między elementami animacji, zwanych klatkami kluczowymi.

Dynamiczne klatki kluczowe

W @scroll-timeline można stosować zaawansowany deklaratywny sposób animacji w CSS, ale wybrana przeze mnie animacja była zbyt dynamiczna. Nie można przełączać się między szerokością auto i nie można dynamicznie tworzyć liczby klatek kluczowych na podstawie długości dzieci.

JavaScript wie jednak, jak uzyskać te informacje, dlatego sami będziemy iterować swoje dzieci i pobieramy obliczone wartości w czasie działania:

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

W przypadku każdego elementu tabnavitem zdestrukturyzuj pozycję offsetLeft i zwracaj ciąg znaków, który używa jej jako wartości translateX. W ten sposób powstaną 4 klatki kluczowe przekształcenia dla animacji. To samo dotyczy szerokości – przy każdym pytaniu należy określić jego szerokość dynamiczną, a następnie zastosować wartość jako wartość klatki kluczowej.

Oto przykładowe dane wyjściowe wyświetlone w oparciu o moje czcionki i ustawienia przeglądarki:

Klatki kluczowe 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)"]

Klatki kluczowe szerokości:

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

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

Podsumowując strategię, wskaźnik karty będzie teraz animowany w 4 klatkach kluczowych w zależności od pozycji przyciągania przewijania sekcji. Punkty przyciągania tworzą wyraźne rozdzielenie między klatkami kluczowymi i nadal podkreślają zsynchronizowany charakter animacji.

Karta aktywna i nieaktywna są wyświetlane z nakładkami VisBug, które pokazują wynik kontrastu obu typów

Użytkownik kieruje animację w ramach swojej interakcji, obserwując szerokość i położenie wskaźnika z jednej sekcji do następnej oraz doskonale śledząc ruch przy przewijaniu.

Być może nie zauważyłeś, ale jestem bardzo dumna z zmiany kolorów, gdy wybiera się wyróżniony element nawigacji.

Niewybrany jaśniejszy szary kolor wydaje się jeszcze bardziej odsunięty, gdy podświetlony element ma większy kontrast. Często zmienia się kolor przejścia tekstu, np. po najechaniu kursorem czy po jego wybraniu, ale zmiana tego koloru przy przewijaniu jest synchronizowana ze wskaźnikiem podkreślenia.

Oto jak to zrobić:

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

Każdy link nawigacyjny karty potrzebuje nowej animacji w kolorze, która śledzi tę samą oś czasu przewijania co wskaźnik podkreślenia. Używam tej samej osi czasu co poprzednio: jego zadaniem jest emitowanie znacznika przy przewijaniu, więc możemy go używać w każdej animacji. Tak jak poprzednio, tworzę w pętli 4 klatki kluczowe i zwracam kolory.

[...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)",
]

Klatka kluczowa w kolorze var(--text-active-color) wyróżnia link. W przeciwnym razie jest to standardowy kolor tekstu. Zagnieżdżona pętla sprawia, że jest ona stosunkowo prosta, ponieważ zewnętrzna pętla to każdy element nawigacji, a pętla wewnętrzna to osobiste klatki kluczowe elementu nawigacji. Sprawdzam, czy zewnętrzna pętla jest taka sama jak pętla wewnętrznej, i używam tego, by wiedzieć, kiedy jest wybrany.

Świetnie mi się to podobało. Ogromnie.

Jeszcze więcej ulepszeń JavaScriptu

Pamiętaj, że podstawa tego, co tu pokażę, działa bez JavaScriptu. Sprawdźmy, jak możemy ulepszyć ten interfejs, gdy jest dostępny JS.

Precyzyjne linki to raczej hasła związane z urządzeniami mobilnymi, ale sądzę, że w tym przypadku intencja ta jest związana z kartami, w których możesz udostępniać adres URL bezpośrednio do zawartości karty. Przeglądarka po otwarciu strony otworzy identyfikator zgodny z hashtagiem adresu URL. Udało mi się znaleźć ten moduł obsługi onload, który działa na różnych platformach.

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

Synchronizacja końca przewijania

Nasi użytkownicy nie zawsze klikają lub nie używają klawiatury, ale czasami po prostu przewijanie bez trudu powinno to być możliwe. Gdy pasek przewijania sekcji przestanie przewijać sekcję, trzeba dopasować jej miejsce na górnym pasku nawigacyjnym.

Czekam na koniec przewijania: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Przy każdym przewijaniu sekcji wyczyść limit czasu sekcji, jeśli jest widoczny, i rozpocznij nowy. Gdy sekcje przestaną być przewijane, nie usuwaj limitu czasu oczekiwania i uruchamiaj je po 100 ms po odpoczynku. Po uruchomieniu wywoła funkcję sprawdzającą, gdzie użytkownik się zatrzymał.

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

  matchingNavItem && setActiveTab(matchingNavItem);
};

Zakładając, że przewijanie zostało przyciągnięte, podzielenie bieżącej pozycji przewijania od szerokości obszaru przewijania powinno skutkować liczbą całkowitą, a nie ułamkiem dziesiętnym. Następnie próbuję pobrać element nawigacyjny z naszej pamięci podręcznej za pomocą obliczonego indeksu i jeśli coś znajdzie, wysyłam dopasowanie do ustawienia jako aktywne.

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

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

Ustawienie aktywnej karty rozpoczyna się od wyczyszczenia wszystkich obecnie aktywnych kart, a następnie przypisania przychodzącego elementu nawigacji do atrybutu stanu aktywnego. Wywołanie scrollIntoView() zawiera ciekawą interakcję z CSS, na którą warto zwrócić uwagę.

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

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

W kodzie CSS narzędzia do przewijania w poziomie zagnieżdżamy zapytanie o multimedia, które powoduje przewijanie smooth, jeśli użytkownik toleruje ruch. JavaScript może swobodnie wywoływać elementy, które pojawiają się na ekranie, a CSS – deklaratywnie zarządzać UX. Przyjemne dla oka małe dopasowanie.

Podsumowanie

Skoro już wiesz, jak to udało mi się osiągnąć, to jak? W ten sposób powstaje zabawna architektura komponentów. Kto utworzy pierwszą wersję z boksami w swojej ulubionej platformie? 🙂

Stwórzmy różne metody i nauczmy się wszystkiego, jak rozwijać się w internecie. Utwórz glitch, napisz mi swoją wersję, a dodam ją do sekcji Remiksy społeczności poniżej.

Remiksy społeczności