Tworzenie komponentu Karty

Podstawowe informacje o tym, jak utworzyć komponent kart podobny do tych, które można znaleźć w aplikacjach na iOS i Androida.

W tym poście chcę podzielić się przemyśleniami na temat tworzenia komponentu kart na potrzeby internetu, który jest elastyczny, obsługuje dane wejściowe z wielu urządzeń i działa w różnych przeglądarkach. Wypróbuj wersję demonstracyjną.

Wersja demonstracyjna

Jeśli wolisz film, obejrzyj tę wersję posta w YouTube:

Przegląd

Karty są powszechnym elementem systemów projektowania, ale mogą przybierać różne kształty i formy. Najpierw były karty na komputery oparte na elemencie <frame>, a teraz mamy płynnie działające komponenty mobilne, które animują treści na podstawie właściwości fizycznych. Wszystkie mają ten sam cel: zaoszczędzić miejsce.

Obecnie podstawą korzystania z kart jest obszar nawigacji z przyciskami, który przełącza widoczność treści w ramce wyświetlania. Wiele różnych obszarów treści zajmuje tę samą przestrzeń, ale są one wyświetlane warunkowo w zależności od przycisku wybranego w nawigacji.

kolaż jest dość chaotyczny ze względu na ogromną różnorodność stylów, które są stosowane w internecie do koncepcji komponentu.
Kolaż stylów projektowania stron internetowych z komponentami kart z ostatnich 10 lat

Taktyki internetowe

Ogólnie rzecz biorąc, ten komponent był dość prosty w budowie dzięki kilku kluczowym funkcjom platformy internetowej:

  • scroll-snap-points – eleganckie interakcje z przesuwaniem i klawiaturą z odpowiednimi pozycjami zatrzymania przewijania;
  • Precyzyjne linki za pomocą skrótów URL w przypadku przeglądarek obsługujących zakotwiczenie przewijania na stronie i udostępnianie
  • Obsługa czytników ekranu z oznaczeniami elementów <a>id="#hash"
  • prefers-reduced-motion, aby włączyć przejścia z przenikaniem i błyskawiczne przewijanie strony.
  • Funkcja internetowa @scroll-timeline w wersji roboczej, która dynamicznie podkreśla i zmienia kolor wybranej karty.

HTML

Podstawowa zasada działania interfejsu użytkownika jest taka: kliknij link, a adres URL będzie odzwierciedlać stan zagnieżdżonej strony. Następnie zobaczysz, jak obszar treści aktualizuje się w miarę przewijania strony przez przeglądarkę do pasującego elementu.

Znajdują się tam elementy strukturalne treści: linki i :target. Potrzebujemy listy linków, do czego świetnie nadaje się <nav>, oraz listy elementów <article>, do czego świetnie nadaje się <section>. Każdy skrót linku będzie pasować do sekcji, dzięki czemu przeglądarka będzie mogła przewijać treści za pomocą kotwic.

Kliknięcie przycisku linku powoduje wysunięcie treści

Na przykład kliknięcie linku automatycznie przenosi fokus na :target artykuł w Chrome 89, bez konieczności używania JS. Użytkownik może wtedy przewijać treść artykułu za pomocą urządzenia wejściowego w zwykły sposób. Jest to treść uzupełniająca, co zostało wskazane w oznaczeniach.

Do uporządkowania kart użyłem tego kodu:

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

Połączenia między elementami <a><article> mogę utworzyć za pomocą właściwości hrefid 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 wypełniłem artykuły różną ilością tekstu lorem ipsum, a linki różnej długości tytułami i zestawem obrazów. Gdy mamy już treści, możemy zacząć tworzyć układ.

Układy przewijania

Ten komponent zawiera 3 rodzaje obszarów przewijania:

  • Nawigacja (różowa) jest przewijana w poziomie.
  • Obszar treści (niebieski) można przewijać w poziomie.
  • Każdy element artykułu (zielony) można przewijać w pionie.
3 kolorowe pola z pasującymi kolorystycznie strzałkami kierunkowymi, które wyznaczają obszary przewijania i wskazują kierunek przewijania.

W przewijaniu biorą udział 2 rodzaje elementów:

  1. Okno
    Pudełko o określonych wymiarach, które ma właściwość overflow style.
  2. Zbyt duża powierzchnia
    W tym układzie są to kontenery listy: linki nawigacyjne, artykuły w sekcjach i treść artykułów.

Układ <snap-tabs>

Wybrany przeze mnie układ najwyższego poziomu to flex (Flexbox). Kierunek ustawiłem na column, więc nagłówek i sekcja są ułożone pionowo. To pierwsze okno przewijania, które ukrywa wszystko, co wykracza poza jego obszar. Nagłówek i sekcja będą wkrótce korzystać z przewijania poza zakres jako osobne strefy.

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

Wracając do kolorowego diagramu z 3 przewijaniami:

  • Element <header> jest teraz przygotowany do pełnienia funkcji kontenera przewijania (różowego).
  • <section> jest przygotowany do bycia kontenerem przewijania (niebieskim).

Ramki, które wyróżniłem poniżej za pomocą VisBug, pomagają nam zobaczyć okna utworzone przez kontenery przewijania.

elementy nagłówka i sekcji są pokryte różowymi nakładkami, które wyznaczają miejsce, jakie zajmują w komponencie.

Układ kart <header>

Kolejny układ jest prawie taki sam: używam flexa do tworzenia kolejności pionowej.

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 powinna przesuwać się poziomo wraz z grupą linków, a ten układ nagłówka pomaga to osiągnąć. Brak elementów pozycjonowanych bezwzględnie.

Elementy nav i span.indicator mają nałożone na nie różowe nakładki, które wyznaczają przestrzeń, jaką zajmują w komponencie.

Następnie style przewijania. Okazuje się, że możemy udostępniać style przewijania między 2 obszarami przewijania w poziomie (nagłówek i sekcja), więc utworzyłem klasę narzędziową .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 z nich wymaga przepełnienia na osi X, ograniczenia przewijania, aby zapobiec nadmiernemu przewijaniu, ukrytych pasków przewijania na urządzeniach dotykowych i przyciągania przewijania, aby blokować obszary prezentacji treści. Kolejność kart na klawiaturze jest dostępna, a wszelkie interakcje naturalnie kierują uwagę. Kontenery przyciągania przewijania mają też ładną interakcję w stylu karuzeli z klawiatury.

Układ nagłówka kart <nav>

Linki nawigacyjne muszą być ułożone w linii bez podziałów wierszy, wyśrodkowane w pionie, a każdy element linku powinien przyciągać do kontenera przyciągania przewijania. Szybkie działanie w przypadku usług porównywania cen w 2021 r.

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

Każdy link ma własny styl i rozmiar, więc w układzie nawigacji wystarczy określić kierunek i przepływ. Unikalne szerokości elementów nawigacji sprawiają, że przejście między kartami jest ciekawe, ponieważ wskaźnik dostosowuje swoją szerokość do nowego celu. W zależności od liczby elementów w tym miejscu przeglądarka wyświetli pasek przewijania lub nie.

elementy a w sekcji nawigacji mają nakładki w kolorze różowym, które wyznaczają miejsce, jakie zajmują w komponencie, a także miejsce, w którym się rozszerzają.

Układ kart <section>

Ta sekcja jest elementem elastycznym i musi zajmować większość miejsca. Musi też utworzyć kolumny, w których będą umieszczane artykuły. Jeszcze raz dziękujemy za szybkie działanie w przypadku CSS 2021! Symbol block-size: 100% rozciąga ten element, aby jak najbardziej wypełnić element nadrzędny, a następnie w swoim układzie tworzy serię kolumn, które mają szerokość 100% elementu nadrzędnego. Wartości procentowe sprawdzają się w tym przypadku doskonale, ponieważ w przypadku elementu nadrzędnego zastosowaliśmy ścisłe ograniczenia.

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śmy mówili „rozwiń się pionowo tak bardzo, jak to możliwe, w sposób natarczywy” (pamiętaj, że nagłówek ustawiliśmy na flex-shrink: 0: chroni on przed tym rozszerzeniem), co ustawia wysokość wiersza dla zestawu kolumn o pełnej wysokości. Styl auto-flow informuje siatkę, że elementy podrzędne mają być zawsze rozmieszczane w linii poziomej bez zawijania, czyli dokładnie tak, jak chcemy – poza oknem nadrzędnym.

elementy artykułu mają nałożone na nie różowe nakładki, które pokazują, ile miejsca zajmują w komponencie i gdzie się rozszerzają;

Czasami trudno mi to zrozumieć. Ten element sekcji mieści się w pudełku, ale utworzył też zestaw pudełek. Mam nadzieję, że wizualizacje i wyjaśnienia są pomocne.

Układ kart <article>

Użytkownik powinien mieć możliwość przewijania treści artykułu, a paski przewijania powinny pojawiać się tylko wtedy, gdy treść nie mieści się w widocznym obszarze. Te elementy artykułu są w odpowiednim miejscu. Są one jednocześnie elementem nadrzędnym i podrzędnym przewijania. Przeglądarka obsługuje tu za nas skomplikowane interakcje z użyciem dotyku, myszy i klawiatury.

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

Postanowiłem, że artykuły będą przyciągane do przewijanej sekcji nadrzędnej. Bardzo podoba mi się, jak elementy linków nawigacyjnych i artykułów przyciągają do początku wiersza w odpowiednich kontenerach przewijania. Wygląda i sprawia wrażenie harmonijnej relacji.

element artykułu i jego elementy podrzędne mają nałożone na nie różowe nakładki, które wyznaczają przestrzeń, jaką zajmują w komponencie, i kierunek, w którym się rozszerzają.

Artykuł jest elementem podrzędnym siatki, a jego rozmiar jest z góry określony jako obszar widoczny, w którym chcemy zapewnić przewijanie. Oznacza to, że nie potrzebuję tutaj żadnych stylów wysokości ani szerokości, muszę tylko określić, jak ma się zachowywać przepełnienie. Ustawiam overflow-y na auto, a następnie przechwytuję interakcje przewijania za pomocą przydatnej właściwości overscroll-behavior.

Podsumowanie 3 obszarów przewijania

W ustawieniach systemu wybrałem opcję „Zawsze pokazuj paski przewijania”. Uważam, że włączenie tego ustawienia jest podwójnie ważne dla układu, ponieważ pozwala mi sprawdzić układ i organizację przewijania.

3 paski przewijania są ustawione tak, aby się wyświetlały, zajmują teraz miejsce w układzie, a nasz komponent nadal wygląda świetnie.

Uważam, że widoczny w tym komponencie margines paska przewijania wyraźnie pokazuje, gdzie znajdują się obszary przewijania, w jakim kierunku można je przewijać i jak ze sobą współdziałają. Zwróć uwagę, że każda z tych ramek okna przewijania jest też elementem nadrzędnym układu typu flex lub siatka.

Narzędzia deweloperskie mogą nam to wizualizować:

obszary przewijania mają nakładki narzędzi siatki i flexbox, które pokazują, ile miejsca zajmują w komponencie i w jakim kierunku się rozszerzają;
Narzędzia deweloperskie w Chromium pokazujące układ elementu nawigacyjnego flexbox pełnego elementów kotwicy, układ sekcji siatki pełnej elementów artykułu oraz elementy artykułu pełne akapitów i elementu nagłówka.

Układy przewijania są gotowe: przyciąganie, precyzyjne linki i dostępność za pomocą klawiatury. Solidne podstawy ulepszeń UX, stylu i przyjemności.

Wyróżniona funkcja

Elementy przyciągane podczas przewijania zachowują zablokowaną pozycję podczas zmiany rozmiaru. Oznacza to, że JavaScript nie będzie musiał niczego wyświetlać po obróceniu urządzenia lub zmianie rozmiaru przeglądarki. Wypróbuj to w trybie urządzenia w Narzędziach deweloperskich w Chromium. Wybierz dowolny tryb inny niż Elastyczny, a następnie zmień rozmiar ramki urządzenia. Zauważ, że element pozostaje widoczny i zablokowany wraz z jego zawartością. Jest to dostępne od czasu, gdy zespół Chromium zaktualizował implementację, aby była zgodna ze specyfikacją. Więcej informacji znajdziesz w tym poście na blogu.

Animacja

Celem animacji jest wyraźne powiązanie interakcji z informacjami zwrotnymi interfejsu. Ułatwia to użytkownikowi odkrywanie wszystkich treści w sposób (miejmy nadzieję) płynny. Będę dodawać ruch z określonym celem i warunkowo. Użytkownicy mogą teraz określać swoje preferencje dotyczące ruchu w systemie operacyjnym, a ja bardzo lubię dostosowywać do nich moje interfejsy.

Podkreślenie karty będzie powiązane z pozycją przewijania artykułu. Przyciąganie to nie tylko ładne wyrównanie, ale też zakotwiczenie początku i końca animacji. Dzięki temu <nav>, który działa jak mini-mapa, jest połączony z treścią. Sprawdzimy preferencje użytkownika dotyczące ruchu zarówno w CSS, jak i w JS. Jest kilka świetnych miejsc, w których warto zachować ostrożność.

Działanie przewijania

Istnieje możliwość poprawy zachowania ruchu zarówno w przypadku :target, jak i element.scrollIntoView(). Domyślnie jest to natychmiastowe. Przeglądarka po prostu ustawia pozycję przewijania. A co, jeśli chcemy przejść do tej pozycji przewijania, zamiast tam mignąć?

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

Wprowadzamy tu ruch, który nie jest kontrolowany przez użytkownika (np. przewijanie), dlatego ten styl stosujemy tylko wtedy, gdy użytkownik nie ma preferencji dotyczących ograniczenia ruchu w systemie operacyjnym. Dzięki temu wprowadzamy przewijanie tylko w przypadku osób, które się na to zgadzają.

Wskaźnik kart

Celem tej animacji jest powiązanie wskaźnika ze stanem treści. Zdecydowałem się na zastosowanie płynnego przejścia kolorów w przypadku border-bottom stylów dla użytkowników, którzy wolą ograniczone animacje, oraz animacji przesuwanej połączonej z przejściem kolorów dla użytkowników, którym nie przeszkadzają animacje.

W narzędziach deweloperskich w Chromium mogę przełączać preferencje i demonstrować 2 różne style przejść. Świetnie się bawiłem, tworząc ten model.

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

Ukrywam .snap-indicator, gdy użytkownik woli ograniczone animacje, ponieważ nie jest mi już potrzebny. Następnie zastępuję go stylami border-block-endtransition. Zwróć też uwagę na interakcję z kartami. Aktywny element nawigacji ma nie tylko podkreślenie w kolorze marki, ale też ciemniejszy kolor tekstu. Aktywny element ma większy kontrast koloru tekstu i jasny akcent podświetlenia.

Wystarczy kilka dodatkowych wierszy kodu CSS, aby użytkownik poczuł, że jego preferencje dotyczące ruchu są uwzględniane. Podoba mi się.

@scroll-timeline

W poprzedniej sekcji pokazałem, jak obsługuję style przejścia z efektem zanikania przy ograniczonym ruchu, a w tej sekcji pokażę, jak połączyłem wskaźnik i obszar przewijania. Teraz pokażemy kilka ciekawych eksperymentów. Mam nadzieję, że cieszysz się tak samo jak ja.

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

Najpierw sprawdzam preferencje użytkownika dotyczące ruchu za pomocą JavaScriptu. Jeśli wynikiem tego działania jest false, co oznacza, że użytkownik woli ograniczone animacje, nie będziemy uruchamiać żadnych efektów animacji związanych z przewijaniem.

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

W momencie pisania tego artykułu obsługa przeglądarek w przypadku@scroll-timeline nie jest dostępna. Jest to wersja robocza specyfikacji, która ma tylko implementacje eksperymentalne. Ma jednak polyfill, którego używam w tym demo.

ScrollTimeline

Zarówno CSS, jak i JavaScript mogą tworzyć osie czasu przewijania, ale ja wybrałem JavaScript, aby móc używać w animacji pomiarów elementów na żywo.

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

Chcę, aby jeden element podążał za pozycją przewijania innego elementu. Tworząc ScrollTimeline, definiuję element sterujący linkiem przewijania, czyli scrollSource. Animacja w internecie zwykle działa w oparciu o globalną oś czasu, ale dzięki niestandardowej funkcji sectionScrollTimeline w pamięci mogę to zmienić.

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

Zanim przejdę do klatek kluczowych animacji, warto wspomnieć, że element śledzący przewijanie, tabindicator, będzie animowany na podstawie niestandardowej osi czasu, czyli przewijania sekcji. To kończy połączenie, ale brakuje ostatniego składnika, czyli punktów stanu, między którymi ma się odbywać animacja, zwanych też klatkami kluczowymi.

Dynamiczne klatki kluczowe

Istnieje bardzo skuteczny, czysto deklaratywny sposób animowania za pomocą CSS z użyciem @scroll-timeline, ale wybrana przeze mnie animacja była zbyt dynamiczna. Nie ma możliwości przejścia między szerokościami auto ani dynamicznego tworzenia liczby klatek kluczowych na podstawie długości elementów podrzędnych.

JavaScript wie, jak uzyskać te informacje, więc sami przeiterujemy po elementach podrzędnych i pobierzemy 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,
  }
);

Dla każdego elementu tabnavitem rozpakuj pozycję offsetLeft i zwróć ciąg znaków, który używa jej jako wartości translateX. Spowoduje to utworzenie 4 klatkowych klatek kluczowych przekształcenia animacji. To samo dotyczy szerokości. Każdy element jest pytany o dynamiczną szerokość, a następnie jest ona używana jako wartość klatki kluczowej.

Oto przykładowe dane wyjściowe na podstawie moich czcionek i ustawień 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"]

Aby podsumować 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 wyraźnie oddzielają klatki kluczowe i dodają animacji wrażenia synchronizacji.

aktywna i nieaktywna karta z nakładkami VisBug, które pokazują wyniki kontrastu dla obu kart;

Użytkownik steruje animacją za pomocą interakcji, obserwując, jak szerokość i pozycja wskaźnika zmieniają się z jednej sekcji na drugą, idealnie dopasowując się do przewijania.

Może tego nie zauważyłeś, ale jestem bardzo dumny z przejścia kolorów, gdy zaznaczony element nawigacyjny staje się wybrany.

Nieaktywny jaśniejszy szary wydaje się jeszcze bardziej cofnięty, gdy wyróżniony element ma większy kontrast. Często stosuje się przejścia kolorów tekstu, np. po najechaniu kursorem lub po wybraniu, ale przejście koloru podczas przewijania, zsynchronizowane ze wskaźnikiem podkreślenia, to już wyższy poziom.

Oto jak to zrobiłem:

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 na karcie musi mieć nową animację koloru, która śledzi tę samą oś czasu przewijania co wskaźnik podkreślenia. Używam tej samej osi czasu co wcześniej: ponieważ jej zadaniem jest emitowanie sygnału podczas przewijania, możemy używać tego sygnału w dowolnym typie animacji. Podobnie jak wcześniej 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)",
]

Kluczowa klatka z kolorem var(--text-active-color) wyróżnia link, a poza tym jest to standardowy kolor tekstu. Zagnieżdżona pętla sprawia, że jest to stosunkowo proste, ponieważ pętla zewnętrzna to każdy element nawigacji, a pętla wewnętrzna to osobiste klatki kluczowe każdego elementu nawigacji. Sprawdzam, czy element pętli zewnętrznej jest taki sam jak element pętli wewnętrznej, i na tej podstawie wiem, kiedy jest on wybrany.

Pisanie tego sprawiło mi dużo radości. Ogromnie.

Jeszcze więcej ulepszeń JavaScriptu

Warto przypomnieć, że to, co tu pokazuję, działa bez JavaScriptu. Zobaczmy, jak możemy to ulepszyć, gdy JavaScript jest dostępny.

Precyzyjne linki to termin bardziej związany z urządzeniami mobilnymi, ale uważam, że w tym przypadku ich cel jest realizowany przez karty, ponieważ możesz udostępnić adres URL bezpośrednio do zawartości karty. Przeglądarka przejdzie na stronie do identyfikatora, który pasuje do skrótu adresu URL. Znalazłem ten onload uchwyt, który wywołuje efekt na różnych platformach.

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

Synchronizacja końca przewijania

Użytkownicy nie zawsze klikają lub używają klawiatury. Czasami po prostu przewijają zawartość ekranu, co jest naturalne. Gdy przewijanie sekcji się zatrzyma, miejsce, w którym się zatrzyma, musi być zgodne z elementem na górnym pasku nawigacyjnym.

Oto jak czekam na zakończenie przewijania:js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Podczas przewijania sekcji wyczyść limit czasu sekcji, jeśli istnieje, i rozpocznij nowy. Gdy sekcje przestaną być przewijane, nie usuwaj limitu czasu i uruchom zdarzenie po 100 ms od zatrzymania. Gdy zostanie uruchomiony, wywołaj funkcję, która próbuje ustalić, gdzie użytkownik przerwał odtwarzanie.

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

  matchingNavItem && setActiveTab(matchingNavItem);
};

Jeśli przewijanie zostało zatrzymane, podzielenie bieżącej pozycji przewijania przez szerokość obszaru przewijania powinno dać liczbę całkowitą, a nie ułamek dziesiętny. Następnie próbuję pobrać element nawigacyjny z pamięci podręcznej za pomocą obliczonego indeksu i jeśli coś znajdę, wysyłam dopasowanie, aby ustawić je jako aktywne.

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

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

Ustawienie aktywnej karty rozpoczyna się od wyczyszczenia obecnie aktywnej karty, a następnie nadania przychodzącemu elementowi nawigacyjnemu atrybutu stanu aktywnego. Wywołanie scrollIntoView() ma ciekawą interakcję z CSS, o której warto wspomnieć.

.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 przyciągania przewijania w poziomie zagnieździliśmy zapytanie o media, które stosuje smooth przewijanie, jeśli użytkownik nie ma problemów z ruchem. JavaScript może swobodnie wywoływać funkcje przewijania elementów do widoku, a CSS może deklaratywnie zarządzać UX. Czasami tworzą naprawdę uroczą parę.

Podsumowanie

Teraz, gdy wiesz, jak to zrobiłem, jak Ty byś to zrobił? Dzięki temu architektura komponentów jest ciekawa. Kto stworzy pierwszą wersję z slotami w swoim ulubionym frameworku? 🙂

Urozmaićmy nasze podejście i poznajmy wszystkie sposoby tworzenia treści w internecie. Utwórz Glitcha, wyślij mi tweeta z Twoją wersją, a ja dodam ją do sekcji Remiksy społeczności poniżej.

Remiksy społeczności