Tạo responsive carousel với CSS

6 min read

Carousel là gì?

Một carousel là một slider tương tác (interactive slider) mà hiển thị nhiều item. Mỗi item có thể là bất cứ thứ gì, trường hợp sử dụng phổ biến là hình ảnh (image) hoặc một thẻ thành phần (card component). Carousel cho phép người dùng hiển thị lựa chọn các item trên màn hình. Thông qua việc tương tác với nó, nó có thể hiển thị các item đã bị ẩn trước đó.

Trước đây, để tạo carousel các bạn cần phải sử dụng javascript để tính toán vị trí chính xác của các item muốn hiển thị trên màn hình. Tuy nhiên giờ đây các bạn có thể dễ dàng tạo một carousel với CSS.

Dưới đây là 1 ví dụ về carousel cho slide ảnh trên trang Polarsteps:

Các trường hợp sử dụng tiềm năng khác của Carousel:

  • Hiển thị các mặt hàng liên quan trong 1 trang bán hàng (webshop).
  • Hiển thị các tin liên quan của trang tin tức.
  • Hiển thị danh sách hình ảnh cho 1 sản phẩm.

Triển khai Carousel bằng Javascript

Cách đây vài năm, để triên khai 1 image carousel bạn cần phải có Javascript. Việc triển khải sẽ bao gồm các vị trí tuyệt đối của các item trong vùng chứa của Carousel bằng CSS. Sau đó bạn cần tính toán kích thước của thẻ bao ngoài của carousel (carousel wrapper) và kích thước thực tế của các item theo tới gian thực bằng cách sử dụng javascript. Sau đó thêm các tương tác(click nút chuyển trái/phải) bằng cách cập nhật lại giá trị left hoặc right hoặc cập nhật lại giá trị transform bằng Javascript. Điều này cho phép bạn triển khai các chức năng cơ bản cho 1 carousel, nếu bạn muốn hỗ trợ tương tác vuốt trên điện tốt hơn thì có thể sử dụng thư viện javascript bên ngoài.

Làm như nào để triển khai Carousel chỉ với CSS?

Để triển khai Carousel bằng CSS bạn có thể sử dụng tính năng CSS scroll snap, bạn sẽ không cần sử dụng bất kỳ thư viện Javascript nào bên ngoài để thực hiện hầu hết các tính năng cơ bản của Carousel.

Để bắt đầu, chúng ta có thể tạo 1 danh sách item có cấu trúc HTML như sau:

<ul class="list">
  <li class="item"><div class="content">Item 1</div></li>
  <li class="item"><div class="content">Item 2</div></li>
  <li class="item"><div class="content">Item 3</div></li>
  <li class="item"><div class="content">Item 4</div></li>
  <li class="item"><div class="content">Item 5</div></li>
</ul>

Sau đó, chúng ta thêm các behaviour:

  • Áp dụng scroll-snap-type cho thẻ bọc ngoài list với giá trị mandatory cho trục mà bạn muốn áp dụng scroll snapping.
  • Áp dụng scroll-snap-align cho các item với giá trị center để đảm bảo trình duyệt sẽ căn chỉnh cho các mục đang active sẽ hiển thị ở giữa thẻ wrapper của bạn.

Sau khi thêm style và thêm khoảng cách giữa các phần tử (item) và xung quanh carousel kết quả sẽ như bên dưới:

.list {
  display: flex;
  gap: 8px;
  padding: 16px;

  list-style: none;
  overflow-x: scroll;
  scroll-snap-type: x mandatory;
}

.item {
  flex-shrink: 0;
  width: 80%;
  height: 90vh;

  background-color: #FFF;

  scroll-snap-align: center;
}

.content {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;

  font-family: sans-serif;
  font-size: 64px;
  font-weight: bold;
}

Kết quả hiển thị trên trình duyệt:

Điều thú vị nhất của tính năng này là nó cũng hoạt động tốt trên điện thoại nếu bạn sử dụng thao tác vuốt trên điện thoại. 😀

Ẩn thanh cuộn

Mặc dù hầu hết các trình duyệt mới hiện nay, thanh cuộn(scrollbar) không còn gây khó chịu như trước nữa nhưng bạn có thể ẩn chúng cho carousel. Chúng ta có thể thực hiện điều đó bằng cách áp dụng thêm code CSS như sau:

.list {
  /* ... */

  /* Hide scrollbar in Firefox */
  scrollbar-width: none;

  /* Hide scrollbar in IE and Edge */
  -ms-overflow-style: none;
}

/* Hide scrollbar in webkit */
.list::-webkit-scrollbar {
  display: none;
}

Kết quả sau khi ẩn thanh cuộn:

Thêm nút điều hướng (Arrow buttons):

Để có trải nghiệm tốt hơn cho người dùng, bạn có thể thêm các nút mũi tên(Arrow buttons) vào cho carousel. Nó sẽ giúp người dùng bằng cách đưa ra dấu hiệu cho người dùng biết có nhiều nội dung hơn để xem. Bây giờ là lúc chúng ta cần thêm 1 chút Javascript để xử lý hành vi cho các nút điều hướng.

Đầu tiên chúng ta thêm các nút Arrow vào HTML và thêm style cho chúng như sau:

<div class="list-wrapper">
  <ul class="list">
    <li class="item"><div class="content">Item 1</div></li>
    <li class="item"><div class="content">Item 2</div></li>
    <li class="item"><div class="content">Item 3</div></li>
    <li class="item"><div class="content">Item 4</div></li>
    <li class="item"><div class="content">Item 5</div></li>
  </ul>
  <button class="button button--previous" type="button">➜</button>
  <button class="button button--next" type="button">➜</button>
</div>
.list-wrapper {
  position: relative;
}

.button {
  position: absolute;
  top: 50%;

  width: 3rem;
  height: 3rem;

  transform: translateY(-50%);
}

.button--previous {
  left: 1.5rem;

  transform: rotate(180deg);
}

.button--next {
  right: 1.5rem;
}

Bây giờ chúng ta có 2 nút mũi tên đơn giản nổi bên trái và bên phải của Carousel như hình dưới:

Tiếp theo, chúng ta sẽ thêm 1 chút mã Javascript để thực hiện các hành vi khi người dùng nhấn vào các nút mũi tên này.

<div class="list-wrapper">
  <ul class="list">
    <li class="item"><div class="content">Item 1</div></li>
    <li class="item"><div class="content">Item 2</div></li>
    <li class="item"><div class="content">Item 3</div></li>
    <li class="item"><div class="content">Item 4</div></li>
    <li class="item"><div class="content">Item 5</div></li>
  </ul>
  <!-- We're calling `handleClick` with a direction argument when the buttons are clicked -->
  <button onclick="handleClick('previous')" class="button button--previous" type="button">➜</button>
  <button onclick="handleClick('next')" class="button button--next" type="button">➜</button>
</div>
<script>
  const list = document.querySelector(".list");

  // We want to know the width of one of the items. We'll use this to decide how many pixels we want our carousel to scroll.
  const item = document.querySelector(".item");
  const itemWidth = item.offsetWidth;

  function handleClick(direction) {
    // Based on the direction we call `scrollBy` with the item width we got earlier
    if(direction === "previous") {
      list.scrollBy({ left: -itemWidth, behavior: "smooth" });
    } else {
      list.scrollBy({ left: itemWidth, behavior: "smooth" });
    }
  }
</script>

Có 1 vài điều cần lưu ý ở đây.

Chúng ta muốn quyết định xem chúng ta muốn cuộn vùng chứa(container) bao nhiêu pixel khi người dùng click các nút mũi tên. Trong ví dụ này, chúng ta thực hiện việc này bằng cách lấy chiều rộng của phần tử đầu tiên(first item) và sử dụng giá trị đó là giá trị pixel để cuộn tới(bạn cũng có thể sử dụng giá trị tĩnh ở đây).

Khi chúng ta gọi hàm scrollBy với giá trị smooth cho tùy chọn behavior. Điều này cho trình duyệt thực hiện việc cuộn 1 cách trơn tru (smooth) theo số lượng pixel mà chúng ta truyền vào. Sau đó chức năng scroll-snap sẽ tiếp tục đảm bảo các item được căn chỉnh ở giữa container.

Kết quả thu được:

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *