Tổng quan cơ bản về cách tạo một thành phần thẻ tương tự như các thành phần có trong ứng dụng iOS và Android.
Trong bài đăng này, tôi muốn chia sẻ suy nghĩ về việc tạo thành phần Thẻ cho trang web thích ứng, hỗ trợ nhiều đầu vào của thiết bị và hoạt động trên nhiều trình duyệt. Xem bản minh hoạ.
Nếu bạn thích xem video hơn, sau đây là phiên bản của bài đăng này trên YouTube:
Tổng quan
Thẻ là một thành phần phổ biến trong hệ thống thiết kế nhưng có thể có nhiều hình dạng và
biểu mẫu. Ban đầu, chúng tôi có các thẻ dành cho máy tính dựa trên phần tử <frame>
, còn bây giờ chúng tôi đã có
các thành phần di động hữu ích giúp tạo ảnh động cho nội dung dựa trên các tính chất vật lý.
Tất cả đều cố gắng làm cùng một điều: tiết kiệm dung lượng.
Ngày nay, thành phần thiết yếu của trải nghiệm người dùng thẻ là khu vực thao tác bằng nút Nút bật/tắt chế độ hiển thị của nội dung trong một khung hiển thị. Nhiều sản phẩm khác nhau các khu vực nội dung có cùng không gian nhưng được biểu thị có điều kiện dựa trên đã chọn trong điều hướng.
![ảnh ghép khá hỗn loạn do có quá nhiều kiểu phong cách mà web đã áp dụng cho khái niệm thành phần](https://web.dev/static/articles/building/a-tabs-component/image/the-collage-is-quite-chao-b6f8cd38f8ddd.png?authuser=19&hl=vi)
Chiến thuật web
Nói chung, tôi thấy thành phần này khá đơn giản để xây dựng, nhờ có một một số tính năng quan trọng của nền tảng web:
scroll-snap-points
để tương tác với bàn phím và thao tác vuốt thanh lịch với vị trí dừng cuộn thích hợp- Đường liên kết sâu thông qua hàm băm URL cho trình duyệt được xử lý hỗ trợ chia sẻ và cố định vị trí cuộn trong trang
- Hỗ trợ trình đọc màn hình với mã đánh dấu phần tử
<a>
vàid="#hash"
prefers-reduced-motion
để bật hiệu ứng chuyển đổi mờ dần và tức thì cuộn trong trang- Tính năng web
@scroll-timeline
dưới dạng bản nháp để tự động gạch chân và thay đổi màu của thẻ đã chọn
Phần tử HTML
Về cơ bản, trải nghiệm người dùng ở đây là: nhấp vào một đường liên kết, để URL đại diện cho Trạng thái trang rồi xem nội dung cập nhật về khu vực nội dung khi trình duyệt cuộn đến phần tử phù hợp.
Trong đó có một số thành phần nội dung có cấu trúc: đường liên kết và :target
. T4
cần danh sách các đường liên kết mà <nav>
phù hợp và danh sách <article>
mà <section>
phù hợp. Mỗi hàm băm liên kết sẽ khớp với một phần,
cho phép trình duyệt cuộn mọi thứ thông qua hoạt động neo.
Ví dụ: khi bạn nhấp vào một đường liên kết, bài viết :target
sẽ tự động được đặt tiêu điểm vào
Chrome 89, không cần JS. Sau đó, người dùng có thể cuộn nội dung bài viết bằng
thiết bị đầu vào của mình như mọi khi. Đây là nội dung bổ sung, như được nêu trong
mã đánh dấu.
Tôi đã sử dụng mã đánh dấu sau để sắp xếp các thẻ:
<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>
Tôi có thể thiết lập kết nối giữa các phần tử <a>
và <article>
bằng
Các thuộc tính href
và id
như sau:
<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>
Tiếp theo, tôi điền nhiều loại lorem vào các bài viết và các đường liên kết kèm độ dài hỗn hợp và tập hợp tiêu đề hình ảnh. Khi có nội dung phù hợp, chúng tôi có thể bắt đầu của bạn.
Bố cục cuộn
Có 3 loại vùng cuộn trong thành phần này:
- Thanh điều hướng (màu hồng) theo chiều ngang có thể cuộn
- Vùng nội dung (màu xanh dương) là theo chiều ngang có thể cuộn
- Mỗi mục trong bài viết (xanh lục) theo chiều dọc có thể cuộn.
![3 hộp nhiều màu sắc có các mũi tên chỉ hướng phù hợp với màu sắc phác thảo các khu vực cuộn và cho biết hướng di chuyển của các hộp đó.](https://web.dev/static/articles/building/a-tabs-component/image/3-colorful-boxes-color-m-da0de23b13551.png?authuser=19&hl=vi)
Có 2 loại phần tử khác nhau liên quan đến thao tác cuộn:
- Một cửa sổ
Một hộp có kích thước được xác định cóoverflow
kiểu thuộc tính. - Một nền tảng quá khổ
Trong bố cục này, đó là các vùng chứa danh sách: nav đường liên kết, bài viết theo mục và nội dung bài viết.
Bố cục <snap-tabs>
Bố cục cấp cao nhất mà tôi chọn là linh hoạt (Flexbox). Tôi đã đặt hướng đi
column
, vì vậy, tiêu đề và phần được sắp xếp theo chiều dọc. Đây là lần đầu tiên chúng tôi
một cửa sổ cuộn và nó ẩn mọi thứ với mục bổ sung được ẩn. Tiêu đề và
sẽ sớm triển khai chức năng cuộn quá mức dưới dạng các vùng riêng lẻ.
<snap-tabs> <header></header> <section></section> </snap-tabs>
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 againstneeding 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }
Quay lại sơ đồ 3 cuộn nhiều màu sắc:
<header>
hiện đã sẵn sàng trở thành (hồng) vùng chứa cuộn.<section>
đã sẵn sàng trở thành nút cuộn (màu xanh dương) vùng chứa.
Những khung hình tôi đã làm nổi bật bên dưới VisBug giúp chúng tôi nhìn thấy cửa sổ vùng chứa cuộn đã tạo.
![các phần tử tiêu đề và phần có lớp phủ màu hồng nổi bật trên đó, phác thảo không gian chúng chiếm trong thành phần](https://web.dev/static/articles/building/a-tabs-component/image/the-header-section-eleme-dd313d7eb1453.png?authuser=19&hl=vi)
Bố cục thẻ <header>
Bố cục tiếp theo gần giống nhau: Tôi sử dụng linh hoạt để tạo thứ tự dọc.
<snap-tabs> <header> <nav></nav> <span class="snap-indicator"></span> </header> <section></section> </snap-tabs>
header { display: flex; flex-direction: column; }
.snap-indicator
phải di chuyển theo chiều ngang cùng với nhóm đường liên kết, và
bố cục tiêu đề này sẽ giúp thiết lập giai đoạn đó. Không có phần tử có vị trí tuyệt đối ở đây!
![các phần tử nav và span.indicator có lớp phủ hotpink trên đó, nêu rõ không gian mà chúng chiếm trong thành phần](https://web.dev/static/articles/building/a-tabs-component/image/the-nav-spanindicator-e-eddaf5b04df39.png?authuser=19&hl=vi)
Tiếp theo là kiểu cuộn. Hoá ra chúng ta có thể chia sẻ kiểu cuộn
giữa 2 vùng cuộn ngang (tiêu đề và phần), nên tôi đã tạo một tiện ích
lớp, .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;
}
}
}
Mỗi cần có mục tràn trên trục x, vùng chứa cuộn để bẫy cuộn quá mức, ẩn thanh cuộn cho thiết bị cảm ứng và cuối cùng là thanh cuộn để khoá nội dung phần trình bày. Bạn có thể truy cập vào thứ tự thẻ bàn phím và hướng dẫn tương tác tập trung một cách tự nhiên. Vùng chứa di chuyển nhanh cũng có kiểu băng chuyền đẹp mắt tương tác từ bàn phím.
Bố cục của tiêu đề thẻ <nav>
Các đường liên kết điều hướng cần được bố trí theo một dòng, không có dấu ngắt dòng theo chiều dọc căn giữa và mỗi mục liên kết phải bám theo vùng chứa scroll-snap. Swift phù hợp với CSS năm 2021!
<nav> <a></a> <a></a> <a></a> <a></a> </nav>
nav { display: flex; & a { scroll-snap-align: start; display: inline-flex; align-items: center; white-space: nowrap; } }
Mỗi kiểu và kích thước liên kết đều tự kích thước, do đó, bố cục điều hướng chỉ cần chỉ định hướng và luồng. Chiều rộng duy nhất trên các mục điều hướng giúp chuyển đổi giữa các thẻ thú vị khi chỉ báo điều chỉnh chiều rộng của nó theo mục tiêu mới. Tuỳ thuộc vào số lượng phần tử nào ở đây, trình duyệt có hiển thị thanh cuộn hay không.
![các phần tử của thanh điều hướng có lớp phủ màu hồng, chỉ rõ không gian chúng chiếm trong thành phần cũng như vị trí chúng tràn](https://web.dev/static/articles/building/a-tabs-component/image/the-elements-the-nav-h-c8efe2b10b412.png?authuser=19&hl=vi)
Bố cục thẻ <section>
Phần này là một mục linh hoạt và cần phải chiếm không gian chủ yếu. Nó
bạn cũng cần tạo các cột để đặt các bài viết vào. Lại nhanh lên nhé
phù hợp với CSS 2021! block-size: 100%
kéo dài phần tử này để lấp đầy
thành phần mẹ nhiều nhất có thể, thì với bố cục của chính nó, công cụ này sẽ tạo ra một loạt
các cột có chiều rộng 100%
chiều rộng của thành phần mẹ. Tỷ lệ phần trăm hoạt động rất tốt ở đây
vì chúng ta đã viết các quy tắc ràng buộc chặt chẽ trên thành phần mẹ.
<section> <article></article> <article></article> <article></article> <article></article> </section>
section { block-size: 100%; display: grid; grid-auto-flow: column; grid-auto-columns: 100%; }
Như thể chúng ta đang nói "mở rộng theo chiều dọc càng nhiều càng tốt, theo cách đẩy mạnh"
(hãy nhớ tiêu đề chúng ta đặt thành flex-shrink: 0
: đó là để chống lại điều này
đẩy mở rộng), giúp thiết lập chiều cao hàng cho một tập hợp các cột có chiều cao đầy đủ. Chiến lược phát hành đĩa đơn
Kiểu auto-flow
yêu cầu lưới luôn bố trí phần tử con theo chiều ngang
dòng, không có xuống dòng, chính xác những gì chúng ta muốn; để tràn cửa sổ chính.
![các thành phần bài viết có lớp phủ màu hồng nổi bật, nêu rõ không gian mà chúng chiếm trong thành phần và vị trí chúng tràn](https://web.dev/static/articles/building/a-tabs-component/image/the-article-elements-hot-924ae524ee8fb.png?authuser=19&hl=vi)
Đôi khi tôi cũng thấy khó khăn khi phải khép mình lại! Phần tử phần này là vừa với một chiếc hộp, mà vừa tạo ra một bộ hộp. Tôi hy vọng hình ảnh và đều rất hữu ích.
Bố cục thẻ <article>
Người dùng phải cuộn được nội dung bài viết và thanh cuộn sẽ chỉ hiển thị nếu có tràn. Các thành phần trong bài viết này được sắp xếp gọn gàng vị trí. Chúng đồng thời là thành phần mẹ cuộn và con cuộn. Chiến lược phát hành đĩa đơn trình duyệt thực sự xử lý một số tương tác chạm, chuột và bàn phím phức tạp cho chúng tôi ở đây.
<article> <h2></h2> <p></p> <p></p> <h2></h2> <p></p> <p></p> ... </article>
article { scroll-snap-align: start; overflow-y: auto; overscroll-behavior-y: contain; }
Tôi chọn đưa các bài viết vào trong thanh cuộn của cha mẹ. Tôi thực sự thích cách các mục đường liên kết điều hướng và các thành phần trong bài viết bắt đầu cùng dòng của vùng chứa cuộn tương ứng. Giao diện hài hoà mối quan hệ.
![phần tử bài viết và các phần tử con của nó có lớp phủ hotpink trên đó, nêu rõ không gian chúng chiếm trong thành phần và hướng chúng tràn](https://web.dev/static/articles/building/a-tabs-component/image/the-article-element-its-8afcb8a6817ff.png?authuser=19&hl=vi)
Bài viết là một lưới con và kích thước của bài viết được định sẵn là khung nhìn mà chúng tôi muốn cung cấp trải nghiệm người dùng cuộn. Điều này có nghĩa là tôi không cần bất kỳ chiều cao hoặc chiều rộng nào kiểu ở đây, tôi chỉ cần xác định cách nó tràn. Tôi đặt overflow-y thành tự động, và sau đó cũng chặn các tương tác cuộn với hành vi cuộn quá mức tiện lợi thuộc tính này.
Tóm tắt 3 vùng cuộn
Trong phần cài đặt hệ thống, tôi đã chọn "luôn hiển thị thanh cuộn" trong phần cài đặt hệ thống. tôi nghĩ khi bạn bật chế độ cài đặt này, điều quan trọng là bố cục là để tôi xem xét bố cục và cách sắp xếp cuộn.
![3 thanh cuộn được thiết lập để hiển thị, hiện đang chiếm không gian bố cục và thành phần của chúng ta vẫn trông tuyệt vời](https://web.dev/static/articles/building/a-tabs-component/image/the-3-scrollbars-are-set-ca02ac51f046d.png?authuser=19&hl=vi)
Tôi nghĩ rằng việc nhìn thấy rãnh thanh cuộn trong thành phần này sẽ giúp hiển thị rõ vị trí các vùng cuộn là, hướng chúng hỗ trợ và cách chúng tương tác với nhau. Cân nhắc xem mỗi khung cửa sổ cuộn này cũng linh hoạt hoặc lưới mẹ với một bố cục.
Công cụ cho nhà phát triển có thể giúp chúng ta trực quan hoá việc này:
![các vùng cuộn có lớp phủ công cụ dạng lưới và hộp linh hoạt, phác thảo không gian mà chúng chiếm trong thành phần và hướng của chúng](https://web.dev/static/articles/building/a-tabs-component/image/the-scroll-areas-grid-f-79f0ae5839bcc.png?authuser=19&hl=vi)
Bố cục cuộn hoàn chỉnh: chụp nhanh, có thể liên kết sâu và bàn phím dễ sử dụng. Có được một nền tảng vững chắc để cải thiện trải nghiệm người dùng, mang lại phong cách và sự thú vị.
Tính năng nổi bật
Cuộn các phần tử được chụp nhanh duy trì vị trí khoá trong khi đổi kích thước. Điều này có nghĩa là JavaScript sẽ không cần đưa bất kỳ nội dung nào vào chế độ xem trên chế độ xoay của thiết bị hoặc trình duyệt đổi kích thước. Hãy dùng thử tính năng này trong Thiết bị của Công cụ cho nhà phát triển Chromium Chế độ theo chọn bất kỳ chế độ nào khác không phải chế độ Thích ứng, sau đó đổi kích thước khung thiết bị. Lưu ý rằng phần tử vẫn hiển thị và bị khoá với nội dung của nó. Đây là vì Chromium đã cập nhật cách triển khai cho phù hợp với thông số kỹ thuật. Sau đây là bài đăng trên blog về chủ đề đó.
Hoạt ảnh
Mục tiêu của ảnh động ở đây là liên kết rõ ràng các lượt tương tác với giao diện người dùng ý kiến phản hồi. Điều này giúp hướng dẫn hoặc hỗ trợ người dùng (hy vọng) khám phá liền mạch tất cả nội dung. Tôi sẽ thêm chuyển động có chủ đích và theo điều kiện. Người dùng hiện có thể chỉ định chuyển động của họ tùy chọn trong hệ điều hành của họ, và tôi hoàn toàn thích phản hồi các lựa chọn ưu tiên của họ trong giao diện của tôi.
Tôi sẽ liên kết dấu gạch dưới thẻ với vị trí cuộn bài viết. Tính năng chụp nhanh không
Căn chỉnh đẹp mắt, đồng thời neo phần đầu và phần cuối của một ảnh động.
Việc này giúp giữ lại <nav>
, hoạt động như một
bản đồ thu nhỏ, được kết nối với nội dung.
Chúng tôi sẽ kiểm tra lựa chọn ưu tiên về chuyển động của người dùng từ cả CSS và JS. Có một
vài địa điểm tuyệt vời đáng chú ý!
Hành vi cuộn
Bạn có thể cải thiện hành vi chuyển động của cả :target
và
element.scrollIntoView()
Theo mặc định, chế độ xem nhanh là tức thì. Trình duyệt chỉ đặt
vị trí cuộn. Nếu chúng ta muốn chuyển sang vị trí cuộn đó,
thay vì nháy mắt ở đó?
@media (prefers-reduced-motion: no-preference) {
.scroll-snap-x {
scroll-behavior: smooth;
}
}
Vì chúng tôi đang giới thiệu chuyển động ở đây và chuyển động mà người dùng không điều khiển (như cuộn), chúng tôi chỉ áp dụng kiểu này nếu người dùng không có lựa chọn ưu tiên trong hệ điều hành của họ xoay quanh việc giảm chuyển động. Với cách này, chúng tôi chỉ giới thiệu tính năng cuộn hiệu quả cho những ai muốn xem xét.
Chỉ báo thẻ
Mục đích của ảnh động này là giúp liên kết chỉ báo với trạng thái
về nội dung. Tôi đã quyết định tô màu mờ kiểu border-bottom
cho người dùng
những người thích chuyển động ít hơn và hình động trượt + làm mờ màu liên kết dạng cuộn
cho những người dùng đã di chuyển được.
Trong Chromium Devtools, tôi có thể bật/tắt tuỳ chọn này và minh hoạ 2 các kiểu chuyển đổi khác nhau. Tôi đã có rất nhiều niềm vui khi xây dựng điều này.
@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;
}
}
Tôi ẩn .snap-indicator
khi người dùng muốn giảm chuyển động vì tôi không muốn vậy
cần đến nữa. Sau đó, tôi thay thế bằng border-block-end
kiểu và
transition
Ngoài ra, hãy lưu ý trong các lượt tương tác với các thẻ, mục điều hướng đang hoạt động không
chỉ có gạch chân thương hiệu, nhưng màu văn bản cũng tối hơn. Chiến lược phát hành đĩa đơn
phần tử hoạt động có độ tương phản màu văn bản cao hơn và màu nhấn dưới ánh sáng tươi sáng.
Chỉ cần một vài dòng CSS bổ sung sẽ khiến mọi người cảm thấy được chú ý (theo nghĩa là chúng ta đang cân nhắc thận trọng về lựa chọn chuyển động của họ). Tôi thích.
@scroll-timeline
Trong phần trên, tôi đã hướng dẫn bạn cách xử lý hiệu ứng chuyển động giảm dần và trong phần này, tôi sẽ hướng dẫn bạn cách liên kết chỉ báo và khu vực cuộn lại với nhau. Đây là một số nội dung thử nghiệm thú vị sắp tới. Tôi hy vọng bạn phấn khích như tôi.
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
);
Trước tiên, tôi kiểm tra lựa chọn ưu tiên về chuyển động của người dùng trong JavaScript. Nếu kết quả của
đây là false
, nghĩa là người dùng muốn giảm chuyển động, nên chúng ta sẽ không chạy bất kỳ
của hiệu ứng chuyển động cuộn liên kết.
if (motionOK) {
// motion based animation code
}
Tại thời điểm viết bài này, hỗ trợ trình duyệt cho
@scroll-timeline
không có giá trị nào. Đó là
thông số kỹ thuật của bản nháp chỉ với
triển khai thử nghiệm. Tuy nhiên, nó có một polyfill mà tôi sử dụng trong này
bản minh hoạ.
ScrollTimeline
Mặc dù CSS và JavaScript đều có thể tạo dòng thời gian cuộn, nhưng tôi đã chọn tham gia JavaScript để tôi có thể sử dụng các phép đo phần tử trực tiếp trong ảnh động.
const sectionScrollTimeline = new ScrollTimeline({
scrollSource: tabsection, // snap-tabs > section
orientation: 'inline', // scroll in the direction letters flow
fill: 'both', // bi-directional linking
});
Tôi muốn 1 nội dung bám theo vị trí cuộn của nội dung khác và bằng cách tạo một
ScrollTimeline
Tôi xác định trình điều khiển của đường liên kết cuộn là scrollSource
.
Thông thường, ảnh động trên web chạy theo kim đánh dấu nhịp độ khung hình toàn cầu, nhưng với
một sectionScrollTimeline
tuỳ chỉnh trong bộ nhớ, tôi có thể thay đổi tất cả những thứ đó.
tabindicator.animate({
transform: ...,
width: ...,
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
Trước khi tìm hiểu về các khung hình chính của hoạt ảnh, tôi nghĩ điều quan trọng là
chỉ ra tín hiệu cuộn, tabindicator
, sẽ được tạo ảnh động dựa trên
trên dòng thời gian tuỳ chỉnh, cuộn phần của chúng tôi. Thao tác này sẽ hoàn tất mối liên kết, nhưng
thiếu thành phần cuối cùng, các điểm có trạng thái để tạo hiệu ứng động, còn được gọi là
khung hình chính.
Khung hình chính động
Có một cách CSS khai báo thuần tuý thực sự mạnh mẽ để tạo ảnh động bằng
@scroll-timeline
, nhưng ảnh động tôi chọn thực hiện quá động. Không có
chuyển đổi giữa chiều rộng auto
và không có cách nào để tạo linh động
một số khung hình chính dựa trên chiều dài của khung hình con.
Tuy nhiên, JavaScript biết cách lấy thông tin đó, vì vậy, chúng tôi sẽ lặp lại con và lấy các giá trị được tính toán trong thời gian chạy:
tabindicator.animate({
transform: [...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`),
width: [...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
Đối với mỗi tabnavitem
, hãy huỷ cấu trúc vị trí offsetLeft
và trả về một chuỗi
sử dụng nó làm giá trị translateX
. Thao tác này tạo ra 4 khung hình chính biến đổi cho
ảnh động. Bạn thực hiện tương tự với chiều rộng, mỗi chiều rộng được hỏi xem chiều rộng động của nó là bao nhiêu
sau đó được dùng làm giá trị khung hình chính.
Dưới đây là kết quả mẫu, dựa trên các lựa chọn ưu tiên của tôi về phông chữ và trình duyệt:
Khung hình chính của 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)"]
Khung hình chính có chiều rộng:
[...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]
Để tóm tắt chiến lược này, chỉ báo thẻ giờ đây sẽ tạo hiệu ứng động trên 4 khung hình chính tuỳ vào vị trí chụp nhanh cuộn của trình cuộn phần. Điểm chụp nhanh tạo ra sự phân định rõ ràng giữa các khung hình chính và thực sự bổ sung cho cảm giác ảnh động được đồng bộ hoá.
![thẻ đang hoạt động và thẻ không hoạt động xuất hiện cùng với lớp phủ VisBug cho thấy các điểm tương phản vượt qua cho cả hai](https://web.dev/static/articles/building/a-tabs-component/image/active-tab-inactive-tab-9604191ea7007.png?authuser=19&hl=vi)
Người dùng điều khiển ảnh động bằng tương tác của họ, thấy chiều rộng và vị trí của chỉ báo thay đổi từ một phần sang phần tiếp theo, theo dõi một cách hoàn hảo với tính năng cuộn.
Có thể bạn không nhận thấy, nhưng tôi rất tự hào về sự chuyển tiếp của màu sắc như mục điều hướng được làm nổi bật sẽ được chọn.
Màu xám nhạt chưa được chọn xuất hiện thậm chí còn nhiều hơn nữa khi mục có độ tương phản cao hơn. Thông thường, bạn sẽ chuyển đổi màu cho văn bản, chẳng hạn như khi di chuột và khi được chọn, nhưng ở cấp độ tiếp theo là chuyển đổi màu đó khi cuộn, đã đồng bộ hoá với chỉ báo gạch chân.
Đây là cách tôi đã thực hiện:
tabnavitems.forEach(navitem => {
navitem.animate({
color: [...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
});
Mỗi liên kết điều hướng thẻ đều cần ảnh động màu mới này để theo dõi cùng một lượt cuộn dòng thời gian làm chỉ báo gạch dưới. Tôi sử dụng cùng một dòng thời gian như trước đây: kể từ vai trò của nó là phát ra một kim đánh dấu nhịp độ khung hình khi cuộn, chúng ta có thể sử dụng kim đánh dấu nhịp độ khung hình đó ở bất kỳ loại nào ảnh động chúng tôi muốn. Như đã làm trước đó, tôi tạo 4 khung hình chính trong vòng lặp và quay lại màu.
[...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)",
]
Khung hình chính có màu var(--text-active-color)
làm nổi bật đường liên kết và
thì đó là màu văn bản chuẩn. Vòng lặp lồng nhau tạo điều kiện
đơn giản, vì vòng lặp bên ngoài là mỗi mục điều hướng còn vòng lặp bên trong là mỗi mục
các khung hình chính cá nhân của navitem. Tôi kiểm tra xem phần tử vòng lặp bên ngoài có giống với
vòng lặp bên trong và sử dụng vòng lặp đó để biết thời điểm vòng lặp được chọn.
Tôi thấy rất vui khi viết được bài này. Rất hữu ích.
Nhiều tính năng nâng cao khác dành cho JavaScript
Cần lưu ý rằng điểm cốt lõi trong những nội dung tôi trình bày ở đây là có tác dụng mà không cần JavaScript. Như đã nói, hãy xem chúng ta có thể nâng cao nó như thế nào khi JS sẵn có.
Liên kết sâu
Đường liên kết sâu là một thuật ngữ dành cho thiết bị di động, nhưng tôi nghĩ mục đích của đường liên kết sâu là
đáp ứng ở đây với các thẻ trong đó bạn có thể chia sẻ một URL trực tiếp đến nội dung của một thẻ. Chiến lược phát hành đĩa đơn
thì trình duyệt sẽ chuyển trong trang đến mã nhận dạng khớp trong hàm băm URL. tôi đã tìm thấy
trình xử lý onload
này đã hoạt động trên nhiều nền tảng.
window.onload = () => {
if (location.hash) {
tabsection.scrollLeft = document
.querySelector(location.hash)
.offsetLeft;
}
}
Kết thúc quá trình đồng bộ hoá cuộn
Người dùng của chúng tôi không phải lúc nào cũng nhấp vào hoặc sử dụng bàn phím, đôi khi họ chỉ cuộn miễn phí (nếu có thể). Khi công cụ cuộn mục dừng cuộn, ở bất cứ đâu mà nút chuyển đến cần phải khớp trong thanh điều hướng trên cùng.
Sau đây là cách tôi đợi kết thúc cuộn:
js
tabsection.addEventListener('scroll', () => {
clearTimeout(tabsection.scrollEndTimer);
tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100);
});
Bất cứ khi nào người dùng cuộn các phần đó, hãy xoá thời gian chờ của phần (nếu có), rồi bắt đầu một chiến dịch mới. Khi các phần ngừng cuộn, đừng xoá thời gian chờ, và bắn 100 mili giây sau khi nghỉ ngơi. Khi lệnh này kích hoạt, hãy gọi hàm tìm cách tìm nơi người dùng đã dừng lại.
const determineActiveTabSection = () => {
const i = tabsection.scrollLeft / tabsection.clientWidth;
const matchingNavItem = tabnavitems[i];
matchingNavItem && setActiveTab(matchingNavItem);
};
Giả sử nút cuộn bị giật, chia vị trí cuộn hiện tại cho chiều rộng của vùng cuộn sẽ cho ra kết quả là một số nguyên chứ không phải số thập phân. Sau đó, tôi cố gắng lấy một mục điều hướng từ bộ nhớ đệm của chúng tôi thông qua chỉ mục đã tính toán này và nếu tìm thấy tôi gửi mã trùng khớp để đặt thành hoạt động.
const setActiveTab = tabbtn => {
tabnav
.querySelector(':scope a[active]')
.removeAttribute('active');
tabbtn.setAttribute('active', '');
tabbtn.scrollIntoView();
};
Việc đặt thẻ đang hoạt động bắt đầu bằng cách xoá mọi thẻ đang hoạt động, sau đó
mục điều hướng đến thuộc tính trạng thái hoạt động. Cuộc gọi đến scrollIntoView()
có một tương tác thú vị với CSS đáng chú ý.
.scroll-snap-x {
overflow: auto hidden;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
Trong CSS tiện ích cuộn ngang chụp nhanh, chúng tôi đã
lồng một truy vấn đa phương tiện áp dụng
smooth
cuộn nếu người dùng chịu được chuyển động. JavaScript có thể tự do tạo
để cuộn các phần tử vào khung hiển thị, còn CSS có thể quản lý trải nghiệm người dùng theo cách tuyên bố.
Khá thú vị mà đôi khi họ tạo ra được.
Kết luận
Giờ bạn đã biết cách tôi thực hiện điều đó, bạn sẽ làm thế nào?! Điều này mang lại niềm vui cấu trúc thành phần! Ai sẽ tạo phiên bản đầu tiên với vị trí trong khung yêu thích của bạn là gì? 🙂
Hãy đa dạng hoá phương pháp tiếp cận và tìm hiểu tất cả các cách xây dựng ứng dụng trên web. Tạo nhiễu sóng, tweet cho tôi phiên bản của bạn và tôi sẽ thêm phiên bản đó vào bản phối lại của cộng đồng phần dưới đây.
Bản phối lại của cộng đồng
- @devnook, @rob_dodson và @DasSurma với Thành phần web: bài viết.
- @jhvanderschee với các nút: Codepen.