Tworzenie komponentu Karty

Podstawowe omówienie sposobu tworzenia komponentu kart podobnego 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 dla internetu, który jest elastyczny, obsługuje wiele urządzeń wejściowych i działa w różnych przeglądarkach. Wypróbuj wersję demonstracyjną.

Wersja demonstracyjna

Jeśli wolisz obejrzeć film, tutaj znajdziesz wersję tego posta na 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łynne komponenty mobilne, które animują treści na podstawie właściwości fizycznych. Wszystkie mają ten sam cel: zaoszczędzić miejsce.

Obecnie podstawą interfejsu kart jest obszar nawigacji przyciskami, który przełącza widoczność treści w ramce wyświetlania. Wiele różnych obszarów treści współdzieli tę samą przestrzeń, ale są one wyświetlane warunkowo na podstawie przycisku wybranego w nawigacji.

Kolaż jest dość chaotyczny ze względu na ogromną różnorodność stylów, które sieć zastosowała do koncepcji komponentu.
Kolaż stylów projektowania stron internetowych z kartami z ostatnich 10 lat

Taktyki dotyczące sieci

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

  • scroll-snap-points, aby zapewnić eleganckie interakcje z użyciem klawiatury i przesuwania palcem z odpowiednimi pozycjami zatrzymania przewijania
  • Precyzyjne linki za pomocą skrótów adresów URL do obsługi zakotwiczania i udostępniania przewijania na stronie obsługiwanego przez przeglądarkę
  • Obsługa czytników ekranu dzięki znacznikom elementów <a> i id="#hash"
  • prefers-reduced-motion do włączania przejść z efektem przenikania i natychmiastowego przewijania na stronie
  • Funkcja internetowa @scroll-timeline w wersji roboczej do dynamicznego podkreślania i zmiany koloru wybranej karty

HTML

Zasadniczo wygoda użytkowników polega na tym, że po kliknięciu linku adres URL reprezentuje stan zagnieżdżonej strony, a następnie obszar zawartości jest aktualizowany, gdy przeglądarka przewija do pasującego elementu.

Są tam pewne elementy strukturalne: linki i :target. Potrzebujemy listy linków, do której świetnie nadaje się element <nav>, oraz listy elementów <article>, do której świetnie nadaje się element <section>. Każdy skrót linku będzie odpowiadał sekcji, co pozwoli przeglądarce przewijać elementy za pomocą kotwiczenia.

Kliknięcie przycisku linku powoduje wysunięcie treści, na których skupia się uwaga

Na przykład kliknięcie linku automatycznie ustawia fokus na artykule :target w Chrome 89 bez konieczności używania JS. Użytkownik może następnie przewijać treść artykułu za pomocą urządzenia wejściowego, tak jak zwykle. Są to treści uzupełniające, jak wskazano w znacznikach.

Do uporządkowania kart użyłem tych znaczników:

<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ę ustanowić 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 wypełniłem artykuły różną ilością tekstu lorem, a linki – różną długością i zestawem tytułów obrazów. Mając treści, nad którymi możemy pracować, możemy zacząć tworzyć układ.

Układy przewijane

W tym komponencie występują 3 różne typy obszarów przewijania:

  • Nawigację (różowy) można przewijać 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 kolorowymi strzałkami kierunkowymi, które wskazują obszary przewijania i kierunek przewijania.

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

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

Układ: <snap-tabs>

Wybrany przeze mnie układ najwyższego poziomu to flex (Flexbox). Ustawiam kierunek na column, aby nagłówek i sekcja były uporządkowane pionowo. To jest nasze pierwsze okno przewijania, które ukrywa wszystko za pomocą właściwości overflow: hidden. Nagłówek i sekcja będą wkrótce korzystać z przewijania poza krawędź 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; } }

Wskazanie kolorowego diagramu z 3 zwojami:

  • <header> jest teraz gotowy do użycia jako (różowy) kontener przewijania.
  • Element <section> jest przygotowany do roli (niebieskiego) kontenera przewijania.

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

elementy nagłówka i sekcji mają nakładki w kolorze jaskrawego różu, które pokazują, ile miejsca zajmują w komponencie.

Układ kart: <header>

Kolejny układ jest prawie taki sam: używam flex, aby utworzyć układ pionowy.

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 powinien 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ą nakładki w kolorze jaskrawego różu, które pokazują, ile miejsca zajmują w komponencie

Następnie style przewijania. Okazuje się, że możemy udostępniać style przewijania między 2 obszarami przewijania w poziomie (nagłówkiem i sekcją), więc utworzyłem klasę pomocniczą .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, domykania przewijania do przechwytywania overscroll, ukrytych pasków przewijania na urządzeniach dotykowych i przewijania z przyciąganiem do blokowania obszarów prezentacji treści. Kolejność kart na klawiaturze jest dostępna, a wszystkie interakcje naturalnie kierują uwagę. Kontenery przewijania z przyciąganiem mają też interakcję w stylu karuzeli z klawiatury.

Układ nagłówka kart <nav>

Linki nawigacyjne muszą być ułożone w jednej linii, bez podziałów wierszy, wyśrodkowane w pionie, a każdy element linku powinien być przyciągany do kontenera przewijania z przyciąganiem. Swift work for 2021 CSS!

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

Każdy link ma własny styl i rozmiar, więc układ nawigacji musi określać tylko kierunek i przepływ. Unikalne szerokości elementów nawigacyjnych 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 przeglądarka może wyświetlić pasek przewijania.

elementy „a” w elemencie „nav” mają nakładki w kolorze jaskrawego różu, które pokazują, ile miejsca zajmują w komponencie i gdzie się przelewają.

Układ kart: <section>

Ta sekcja jest elementem elastycznym i musi być dominującym elementem zajmującym miejsce. Musi też utworzyć kolumny, w których będą umieszczane artykuły. Jeszcze raz dziękuję za szybką pracę nad CSS 2021. block-size: 100% rozciąga ten element, aby jak najbardziej wypełnić element nadrzędny, a następnie tworzy serię kolumn o szerokości 100% elementu nadrzędnego. Wartości procentowe sprawdzają się tu świetnie, ponieważ określiliśmy silne ograniczenia dla 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śmy powiedzieli „rozwiń się w pionie tak bardzo, jak to możliwe, w nachalny sposób” (pamiętaj o nagłówku, który ustawiliśmy na flex-shrink: 0: to obrona 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 ułożone w poziomej linii, bez zawijania, czyli dokładnie tak, jak chcemy, aby przepełniały okno nadrzędne.

elementy artykułu mają nakładki w kolorze jaskrawego różu, które pokazują, ile miejsca zajmują w komponencie i gdzie się przelewają;

Czasami trudno mi to ogarnąć. Ten element sekcji pasuje do pola, ale tworzy też zestaw pól. 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ę na ekranie. Te elementy artykułu są umieszczone w odpowiednim miejscu. Są one jednocześnie elementem nadrzędnym i podrzędnym przewijania. Przeglądarka obsługuje za nas skomplikowane interakcje dotykowe, 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;
}

Zdecydowałem, że artykuły będą przyciągane do nadrzędnego elementu przewijania. Bardzo podoba mi się, jak elementy linków nawigacyjnych i elementy artykułów przyciągają się do początku kontenerów przewijania. Wygląda na to, że jest to harmonijna relacja.

Element artykułu i jego elementy podrzędne mają nakładki w kolorze jaskrawego różu, które pokazują, ile miejsca zajmują w komponencie i w jakim kierunku się rozszerzają.

Artykuł jest elementem podrzędnym siatki, a jego rozmiar jest wstępnie określony jako obszar widocznego obszaru, w którym chcemy zapewnić przewijanie. Oznacza to, że nie potrzebuję tu ż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

Poniżej w ustawieniach systemu wybrałem opcję „Zawsze pokazuj paski przewijania”. Uważam, że to podwójnie ważne, aby układ działał z włączonym tym ustawieniem, ponieważ muszę sprawdzić układ i orkiestrację przewijania.

3 paski przewijania są ustawione jako widoczne, zajmują teraz miejsce w układzie, a nasz komponent nadal wygląda świetnie.

Myślę, że widoczność rynny paska przewijania w tym komponencie pomaga wyraźnie pokazać, gdzie znajdują się obszary przewijania, jaki kierunek obsługują i jak ze sobą współdziałają. Zastanów się, jak każda z tych ramek okna przewijania jest też elementem nadrzędnym układu flex lub grid.

Narzędzia deweloperskie mogą pomóc nam to zwizualizować:

obszary przewijania mają nakładki narzędzi siatki i flexbox, które pokazują przestrzeń, jaką zajmują w komponencie, oraz kierunek, w którym się przelewają;
Narzędzia deweloperskie Chromium, pokazujące układ elementu nawigacyjnego flexbox z elementami kotwicy, układ sekcji siatki z elementami artykułu oraz elementy artykułu z akapitami i elementem nagłówka.

Układy przewijania są kompletne: przyciąganie, możliwość precyzyjnego linkowania i dostępność za pomocą klawiatury. Solidne podstawy do ulepszania UX, stylu i atrakcyjności.

Wyróżniona funkcja

Elementy podrzędne z przewijaniem z przyciąganiem zachowują zablokowaną pozycję podczas zmiany rozmiaru. Oznacza to, że JavaScript nie musi niczego wyświetlać po obróceniu urządzenia lub zmianie rozmiaru okna przeglądarki. Wypróbuj tę funkcję w trybie urządzenia w Narzędziach deweloperskich w Chromium, wybierając dowolny tryb inny niż Responsywny, a następnie zmieniając rozmiar ramki urządzenia. Zauważ, że element pozostaje widoczny i zablokowany wraz z zawartością. Ta funkcja jest dostępna od czasu, gdy Chromium zaktualizowało implementację, aby była zgodna ze specyfikacją. Tutaj znajdziesz post na blogu na ten temat.

Animacja

Celem animacji jest wyraźne powiązanie interakcji z informacjami zwrotnymi w interfejsie. Dzięki temu użytkownik może łatwo odkryć wszystkie treści. Będę dodawać ruch z zamysłem i warunkowo. Użytkownicy mogą teraz określić ustawienia ruchu w systemie operacyjnym, a ja bardzo lubię reagować na ich preferencje w moich interfejsach.

Połączę podkreślenie karty z pozycją przewijania artykułu. Przyciąganie nie tylko zapewnia ładne wyrównanie, ale też zakotwicza początek i koniec animacji. Dzięki temu <nav>, który działa jak minimapa, jest połączony z treścią. Sprawdzimy preferencje użytkownika dotyczące ruchu zarówno w CSS, jak i JS. Jest kilka miejsc, w których warto zachować ostrożność.

Działanie przewijania

Można poprawić zachowanie ruchu zarówno w przypadku :target, jak i element.scrollIntoView(). Domyślnie jest to natychmiast. Przeglądarka ustawia tylko pozycję przewijania. A co, jeśli chcemy przejść do tej pozycji przewijania, zamiast od razu się tam znaleźć?

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

Ponieważ wprowadzamy tu ruch, którego użytkownik nie kontroluje (np. przewijanie), stosujemy ten styl tylko wtedy, gdy użytkownik nie ma w systemie operacyjnym ustawionych preferencji dotyczących ograniczenia ruchu. W ten sposób wprowadzamy ruch przewijania tylko dla osób, które nie mają nic przeciwko temu.

Wskaźnik kart

Celem tej animacji jest ułatwienie powiązania wskaźnika ze stanem treści. Zdecydowałem się na płynne przejście kolorów border-bottom dla użytkowników, którzy wolą ograniczenie ruchu, oraz na animację przesuwania i zanikania kolorów dla użytkowników, którym ruch nie przeszkadza.

W Narzędziach deweloperskich Chromium mogę przełączyć preferencje i zademonstrować 2 różne style przejścia. Tworzenie tego było świetną zabawą.

@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 element .snap-indicator, gdy użytkownik preferuje ograniczony ruch, ponieważ nie jest mi już potrzebny. Następnie zastępuję go stylami border-block-end i transition. Zwróć też uwagę, że w interakcji z kartami aktywny element nawigacyjny ma nie tylko podkreślenie w kolorze marki, ale też ciemniejszy kolor tekstu. Aktywny element ma wyższy kontrast kolorów tekstu i jasny akcent podświetlenia.

Wystarczy kilka dodatkowych wierszy kodu CSS, aby użytkownik poczuł się zauważony (w tym sensie, że z szacunkiem podchodzimy do jego preferencji dotyczących ruchu). Jest doskonała.

@scroll-timeline

W poprzedniej sekcji pokazałem, jak obsługuję style przenikania przy ograniczeniu ruchu, a w tej sekcji pokażę, jak połączyłem wskaźnik i obszar przewijania. Teraz przejdziemy do ciekawych funkcji eksperymentalnych. Mam nadzieję, że jesteście tak samo podekscytowani jak ja.

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

Najpierw sprawdzam preferencje użytkownika dotyczące ruchu w kodzie JavaScript. Jeśli wynik to false, co oznacza, że użytkownik preferuje ograniczony ruch, nie uruchomimy żadnych efektów ruchu związanych z przewijaniem.

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

W momencie pisania tego artykułu żadna przeglądarka nie obsługuje @scroll-timeline. Jest to specyfikacja robocza, która ma tylko implementacje eksperymentalne. Ma jednak polyfill, którego używam w tym demo.

ScrollTimeline

Chociaż zarówno CSS, jak i JavaScript mogą tworzyć osie czasu przewijania, 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 linku przewijania, czyli scrollSource. Zwykle animacja w internecie jest uruchamiana w ramach globalnego cyklu czasowego, ale dzięki niestandardowemu elementowi sectionScrollTimeline w pamięci mogę to zmienić.

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

Zanim przejdę do klatek kluczowych animacji, warto zwrócić uwagę, że element śledzący przewijanie, tabindicator, będzie animowany na podstawie niestandardowej osi czasu, czyli przewijania sekcji. To kończy łączenie, ale brakuje ostatniego elementu, czyli punktów stanowych, między którymi ma odbywać się animacja, czyli klatek kluczowych.

Dynamiczne klatki kluczowe

Istnieje bardzo skuteczny sposób animowania za pomocą @scroll-timeline, ale animacja, którą wybrałem, była zbyt dynamiczna. Nie ma możliwości przejścia między szerokością auto ani dynamicznego tworzenia klatek kluczowych na podstawie długości elementów podrzędnych.

JavaScript wie jednak, jak uzyskać te informacje, więc sami przejdziemy przez elementy podrzędne 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 wyodrębnij pozycję offsetLeft i zwróć ciąg znaków, który używa jej jako wartości translateX. Spowoduje to utworzenie 4 klatek kluczowych przekształcenia dla animacji. To samo dotyczy szerokości. Każdy element jest pytany o dynamiczną szerokość, która jest następnie używana jako wartość klatki kluczowej.

Oto przykładowy wynik 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"]

Podsumowując strategię: wskaźnik karty będzie teraz animowany w 4 klatkach kluczowych w zależności od pozycji przewijania z przyciąganiem w elemencie przewijania sekcji. Punkty przyciągania tworzą wyraźne rozgraniczenie między klatkami kluczowymi i naprawdę zwiększają wrażenie synchronizacji animacji.

Aktywna i nieaktywna karta są wyświetlane z nakładkami VisBug, które pokazują, że obie karty mają odpowiedni kontrast.

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 nie zwróciliście na to uwagi, ale jestem bardzo dumny z przejścia kolorów, gdy zaznaczony element nawigacji zostaje wybrany.

Niezaznaczony jaśniejszy szary wydaje się jeszcze bardziej cofnięty, gdy zaznaczony element ma większy kontrast. Często zmienia się kolor tekstu, np. po najechaniu kursorem i po zaznaczeniu, ale jeszcze lepiej jest zmieniać kolor podczas przewijania, zsynchronizowany ze wskaźnikiem podkreślenia.

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 karty potrzebuje tej nowej animacji 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 rolą jest emitowanie tyknięcia podczas przewijania, możemy użyć tego tyknięcia w dowolnym typie animacji. Podobnie jak wcześniej, tworzę 4 klatki kluczowe w pętli 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 z kolorem var(--text-active-color) wyróżnia link, a w pozostałych przypadkach 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 nawigacyjny, a pętla wewnętrzna to osobiste klatki kluczowe każdego elementu nawigacyjnego. Sprawdzam, czy element pętli zewnętrznej jest taki sam jak element pętli wewnętrznej, i na tej podstawie określam, kiedy jest on wybrany.

Pisanie tego sprawiło mi wiele przyjemności. Ogromnie.

Jeszcze więcej ulepszeń JavaScriptu

Warto przypomnieć, że podstawowe funkcje, które tu pokazuję, działają bez JavaScriptu. Zobaczmy, jak możemy ulepszyć ten proces, gdy JS jest dostępny.

Precyzyjne linki to raczej termin związany z urządzeniami mobilnymi, ale myślę, że ich cel jest tutaj 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 jest dopasowany do hasha adresu URL. Zauważyłem, że ten moduł obsługi onload 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, czasem po prostu swobodnie przewijają stronę, co powinno być możliwe. Gdy przewijanie sekcji się zatrzyma, miejsce, w którym się zatrzymało, musi być dopasowane do górnego paska nawigacyjnego.

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

Za każdym razem, gdy sekcje są przewijane, wyczyść limit czasu sekcji, jeśli istnieje, i rozpocznij nowy. Gdy sekcje przestaną być przewijane, nie czyść limitu czasu i uruchom po 100 ms od zatrzymania. Gdy się uruchomi, wywołaj funkcję, która spróbuje ustalić, 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 z przyciąganiem zostało zastosowane, 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ą tego obliczonego indeksu. Jeśli coś znajdę, wysyłam dopasowanie do aktywacji.

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

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

Ustawianie aktywnej karty rozpoczyna się od wyczyszczenia aktualnie aktywnej karty, a następnie nadania przychodzącemu elementowi nawigacyjnemu atrybutu stanu aktywnego. Wywołanie scrollIntoView() ma 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 z przyciąganiem w poziomie zagnieździliśmy zapytanie o media, które stosuje przewijanie smooth, jeśli użytkownik toleruje ruch. JavaScript może swobodnie wywoływać elementy przewijania do widoku, a CSS może zarządzać UX w sposób deklaratywny. Czasami tworzą całkiem udaną parę.

Podsumowanie

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

Zróżnicujmy nasze podejście i poznajmy wszystkie sposoby tworzenia stron internetowych. Utwórz Glitch, wyślij mi tweeta ze swoją wersją, a ja dodam ją do sekcji Remiksy społeczności poniżej.

Remiksy społeczności