建立分頁元件

概略說明如何建立類似 iOS 和 Android 應用程式中的分頁元件。

在這篇文章中,我想分享如何打造網頁版的分頁元件 回應式網頁、支援多種裝置輸入來源,且適用於各種瀏覽器。 歡迎查看示範內容

示範

如果您喜歡看影片,請參考這篇文章的 YouTube 版本:

總覽

分頁是設計系統的常見元件,但也可能是 表單。首先是在 <frame> 元素上建立桌面分頁,現在我們 或活潑的行動版元件,可根據物理特性呈現內容動畫。 他們都想做一樣的事:節省空間。

如今,分頁使用者體驗的基本要素為按鈕導覽區 可在顯示畫面中切換內容的顯示設定。許多不同的 內容區域會共用相同的空間,但會根據 已在導覽中選取按鈕。

因為網頁運用了各種網頁樣式的多樣性,所以美術拼貼非常混亂
過去 10 年來,分頁元件的網頁設計樣式美術拼貼

網路戰術

我認為這個元件很容易建構,因為 幾項重要的網路平台功能:

  • scroll-snap-points 可讓你享受優雅滑動和鍵盤互動的功能 適當的捲動停止位置
  • 透過網址雜湊產生的深層連結, 瀏覽器處理網頁內捲動錨定和共用支援
  • 螢幕閱讀器支援 <a>id="#hash" 元素標記
  • prefers-reduced-motion:用於啟用交叉漸變轉場效果和即時效果 網頁內捲動
  • @scroll-timeline 網路功能草稿,提供動態底線及 顏色:變更所選分頁

HTML

基本上,這裡的使用者體驗為:按一下連結,使用代表巢狀結構的網址 然後內容區域會在瀏覽器捲動至 比對元素

連結中包含結構性內容成員:連結和 :target。三 需要連結清單 (<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> hrefid 屬性如下:

<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 種不同的捲動區域:

  • 導覽 (粉紅色) 為橫向 可捲動
  • 內容區域 (blue) 是水平方向 可捲動
  • 每個文章項目 (綠色) 皆為垂直 可捲動。
3 個彩色方塊以及符合方向箭頭的顏色,這些方塊代表捲動區域的外框,以及使用者捲動的方向。

捲動作業涉及 2 種不同的元素:

  1. 視窗
    已定義尺寸的方塊,具有 overflow 屬性樣式。
  2. 超大型介面
    在這個版面配置中,以下是清單容器:nav 連結、版面文章和文章內容

<snap-tabs>」版面配置

我選擇的頂層版面配置是彈性版面配置 (Flexbox)。我將方向設為: column,因此標頭和區段會垂直排序。這是我們第一個 則隱藏已隱藏溢位的所有項目。標題和 部分會採用個別可用區,盡快使用過度捲動功能。

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> 已準備好進行 (藍色) 捲動 都會在 Docker 容器中執行

我在下方醒目顯示的影格 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> 版面配置

導覽連結必須排成一行,不可垂直分隔 且每個連結項目都應貼齊捲軸容器。Swift 是 2021 CSS 的幕後推手!

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

  & a {
    scroll-snap-align: start;

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

每個連結樣式和大小本身,因此導覽版面配置只需要指定 包括指示方向和流程導覽項目使用獨特的寬度,導致分頁轉換 有趣的操作,因為指標會將寬度調整為新的目標。取決於 元素,瀏覽器就會轉譯捲軸。

導覽的元素上會顯示熱粉紅色重疊元素,概述這些元素在元件中佔用的空間,以及這些元素溢出的位置

分頁 <section> 版面配置

本節是彈性商品,需要是空間的主要消費者。這項服務 也需要建立欄,以便放置文章。再接再厲 能協助處理 CSS 2021 的相關工作!block-size: 100% 會延展此元素來填滿 然後針對自己的版面配置建立一系列的 屬於 100% 欄 (即父項的寬度)。百分比值很適合 因為我們已對父項編寫嚴格限制

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

就像這樣說:「盡可能以強而有力地垂直擴展」 (請記住,我們將標頭設為 flex-shrink: 0 的標頭是防範這個問題的 展開傳送),設定一組整個高度欄的列高。 auto-flow 樣式會指示格線一律以水平方向排列子項 完全符合需求即可讓父項視窗溢位

文章元素上會有熱粉重疊效果,概述這些元素在元件中佔有的空間,以及這些元素溢出的位置

有時候我覺得這很難繞頭來!這個區段元素是 不僅固定,同時還製造出一組包裝盒希望畫面內容 解釋是有幫助的

分頁 <article> 版面配置

使用者必須能夠捲動文章內容,且捲軸應 只有在發生溢位時才會顯示這些文章元素整潔清晰 位置。同時是捲動父項和捲動子項。 瀏覽器在處理某些棘手的觸控、滑鼠和鍵盤互動 。

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

我選擇讓文章在家長的捲動器中對齊。我真的很喜歡 導覽連結項目和文章元素如何對齊內嵌開始 顯示各自的捲動容器外觀和感覺就像和諧一樣 關係

文章元素及其子項元素重疊在一起,以概述這些在元件中佔有的空間,以及這些元素溢出的方向

文章是格線子項,且大小已預先定義為可視區域 這次我們想要提供捲動式使用者體驗這表示我不需要任何高度或寬度 因此只需定義溢位的方式我將 overflow-y 設為自動 然後再利用便利的過度捲動行為 資源。

3 則捲動區域重點回顧

我在系統設定中,選擇了「一律顯示捲軸」。我認為 開啟這項設定對版面配置至關重要,因為 檢視版面配置和捲動編排方式

將 3 捲軸設為顯示,現在會消耗版面配置空間,而且我們的元件仍看起來良好

我認為這個元件中的捲軸空白邊有助於清楚顯示 捲動區域、輔助方向及互動方式 互相啟動。思考一下這些捲動視窗頁框如何具有彈性或 放進版面配置。

開發人員工具可以幫助我們以視覺化方式呈現以下項目:

捲動區域會顯示格線和 Flexbox 工具疊加層,概述這些項目在元件中佔有的空間,以及溢位的方向
Chromium 開發人員工具,顯示滿錨元素的 Flexbox 導覽元素版面配置。 滿版報導元素的格狀版面版面配置,以及 是由段落和標題元素所組成

捲動版面配置已完成:貼齊、可深層連結和鍵盤 方便存取提供堅實基礎,提供更優質的使用者體驗、強化風格,並帶來愉悅的使用體驗。

功能醒目顯示

捲動貼齊的子項在調整大小時,會維持鎖定位置。也就是說 JavaScript 不需要在裝置旋轉或瀏覽器中顯示任何畫面 調整大小。在 Chromium 開發人員工具 Device 中試用 模式,擁有者: 選取「回應式」以外的任何模式,然後調整裝置邊框大小。 請注意,元素會保持在檢視畫面中,並隨著內容鎖定。這意味著 。接下來 相關網誌文章

動畫

這裡的動畫目標是清楚呈現使用者與 UI 的互動情形 提供意見回饋。這有助於引導或協助使用者完成 (希望) 瀏覽所有內容我將加入有目的的動態效果 。使用者現在可以指定自己的動作 建議 我非常喜歡在介面中回應他們的偏好。

我會連結一個底線和文章捲動位置。貼齊功能沒有 同時也錨定在動畫的開始和結尾。 這會保留 <nav>,如同 mini-map 連結至內容。 我們會透過 CSS 和 JS 檢查使用者的動作偏好設定。還有 這裡列出幾個值得考慮的好地方!

捲動行為

這表示有機會強化 :targetelement.scrollIntoView()。預設為立即安裝瀏覽器只會設定 也就是捲動位置如果想回到捲動位置 而不是眨眼?

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

因為要導入動態效果,而且使用者無法控制動態效果 (例如捲動) 時,只有在使用者沒有偏好設定時,才會套用這個樣式 他們的作業系統如此一來,我們只會 其中一種方式

分頁指標

此動畫的目的是協助建立指標與狀態的關聯 內容的用途我決定為使用者加上交錯的border-bottom樣式 他們偏好減少動作,以及捲動連結滑動操作和淡出的動畫效果 輕鬆吸引出合適的動態使用者

在 Chromium 開發人員工具中,我可以切換偏好設定,並示範 不同的轉場樣式我對這個建築有相當多的樂趣。

@media (prefers-reduced-motion: reduce) {
  snap-tabs > header a {
    border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
    transition: color .7s ease, border-color .5s ease;

    &:is(:target,:active,[active]) {
      color: var(--text-active-color);
      border-block-end-color: hsl(var(--accent));
    }
  }

  snap-tabs .snap-indicator {
    visibility: hidden;
  }
}

我不想在使用者偏好減少動作時隱藏 .snap-indicator 不需要。接著替換為 border-block-end 樣式和 transition。另請注意,在分頁互動中,使用中的導覽項目 只有品牌底線醒目顯示,但文字顏色也會較深。 使用中的元素具有較高的文字色彩對比,並具備亮淺膚色的強調效果。

CSS 供應商多出幾行文字,會讓大家覺得 還很謹慎地尊重他們的動作偏好)。我喜歡

@scroll-timeline

在上一節中,我展示了處理減少動作交叉淡出的方式 在這一節中,我將示範我如何將指標連結 可以一次捲動區域這裡有一些有趣的實驗功能。希望你 都跟我一樣興奮

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

我會先透過 JavaScript 檢查使用者的動作偏好設定。如果造成 這是 false,表示使用者偏好減少動作,則我們不會執行 以及捲動連結動作效果

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

撰寫本文時,瀏覽器支援 @scroll-timeline 為任何值。這是 草稿規格 以及實驗功能。但有 polyfill;在這裡 示範。

ScrollTimeline

雖然 CSS 和 JavaScript 都可以建立捲動時間軸,但我選擇 JavaScript,這樣我可以在動畫中使用即時元素測量。

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

我想讓 1 個事物跟著另一個人的捲動位置,並建立一個 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 值。這樣會為 寬度也是一樣,系統會詢問各自的動態寬度為何 然後使用作為主要畫面格值

以下是根據我的字型和瀏覽器偏好設定所輸出的範例:

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) 的主要畫面格會醒目顯示連結, 否則就會是標準文字顏色。其中的巢狀結構循環層會 因為外部迴圈是每個導覽項目,而內部迴圈 navitem 的個人主要畫面格。我檢查外部迴圈元素是否 並用該程式碼瞭解是否已選取

我這邊寫了很多樂趣,愛死了

進一步強化 JavaScript

溫馨提醒,在這裡說明的重點在沒有問題的情況下 JavaScript。儘管如此,我們來看看如何在 JS 與 廣告。

深層連結較常見的行動裝置字詞,但我認為深層連結的用意是 可讓您在標籤中直接分享網址到分頁內容 瀏覽器會在網頁中前往與網址雜湊中相符的 ID。我找到 這個 onload 處理常式能夠跨平台發揮效益。

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

捲動結尾同步處理

我們的使用者不一定每次都會點擊或使用鍵盤 重點在於瀏覽內容區段捲動器停止時 。任何位置都必須與頂端導覽列一致。

以下是等待捲動結束時間的方法: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

在捲動區段時清除區塊逾時 (如果有的話)。 開始新的頻道捲動區段停止捲動時,不要清除逾時。 並在休息 100 毫秒後觸發呼叫時,系統會呼叫這個函式, 與使用者停下的地方。

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

  matchingNavItem && setActiveTab(matchingNavItem);
};

假設捲動已貼齊捲動位置,將目前的捲動位置與寬度分割 捲動區域的運算區域應為整數,而非小數。然後嘗試 透過這個計算的索引從快取中擷取導覽項目,如果找到 那我就傳送相符項目以便啟用

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

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

如要設定使用中的分頁,請先清除目前使用中的分頁,然後授予 傳入的導覽項目即為使用狀態屬性。對 scrollIntoView() 的呼叫 能讓你與 CSS 產生有趣的互動,值得注意。

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

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

在水平捲動貼齊公用程式 CSS 中 巢狀媒體查詢,且適用 如果使用者可容忍動作,則 smooth 會捲動。JavaScript 可以讓您 呼叫將元素捲動至檢視畫面中,而 CSS 可透過宣告方式管理使用者體驗。 有時他們也會為自己帶來愉悅的幫手。

結論

現在你知道該怎麼做了,你會怎麼做?!這會帶來一些樂趣 元件架構!誰打算在第 1 版和 最喜歡的架構呢?🙂

讓我們來體驗多元的方法,瞭解透過網路建立內容的所有方式。 建立 GlitchTwitter 推文 並將其加入社群重混 以下章節。

社群重混作品