JSFiddle - React, Tailwind, and code Playground

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<h1 style="text-align: center; position: absolute; width: 100%; z-index: 1; color: #ccc;">JavaScript 系列三:練習7 ──
  carousel 輪播元件
</h1>

<div class="carousel">
  <img src="https://picsum.photos/id/38/1500/900" alt="" class="">
  <img src="https://picsum.photos/id/56/1500/900" alt="" class="">
  <img src="https://picsum.photos/id/101/1500/900" alt="" class="">
  <img src="https://picsum.photos/id/103/1500/900" alt="" class="">
  <img src="https://picsum.photos/id/84/1500/900" alt="" class="">

  <button class="slide-btn" id="slidePre" onclick="swipeSlide(-1)"><i class="fa-solid fa-chevron-left"></i></button>
  <button class="slide-btn" id="slideNext" onclick="swipeSlide(+1)"><i class="fa-solid fa-chevron-right"></i></button>

  <div class="pages">
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
  </div>
</div>

CSS

* {
  padding: 0;
  margin: 0;
}

.carousel {
  width: 100%;
  height: 100vh;
  position: relative;
  overflow: hidden;
}

.carousel .slide-btn {
  width: 50px;
  height: 50px;
  line-height: 50px;
  font-size: 24px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  overflow: hidden;
  color: rgba(200, 200, 200, 0.3);
  background-color: transparent;
  transition: 0.3s;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

.carousel .slide-btn#slidePre {
  left: 20px;
}

.carousel .slide-btn#slideNext {
  right: 20px;
}

.carousel .slide-btn:hover {
  color: rgba(10, 10, 10, 1);
  background-color: rgba(240, 240, 240, 0.5);
}

.carousel img {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  opacity: 0;
  transition: 0.5s;
}

.carousel img.active {
  opacity: 1;
}

.pages {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 30px;
  text-align: center;
  font-size: 0;
}

.pages .bar {
  display: inline-block;
  width: 70px;
  height: 100%;
  margin: 0 10px;
  position: relative;
  cursor: pointer;
}

.pages .bar::after {
  position: absolute;
  content: "";
  display: block;
  left: 10px;
  right: 10px;
  height: 5px;
  top: 0;
  bottom: 0;
  margin: auto;
  border-radius: 5px;
  background-color: rgba(240, 240, 240, 0.3);
  transition: 0.5s;
}

.pages .bar.active::after {
  background-color: rgba(240, 240, 240, 0.7);
}

JavaScript

const imgCount = document.querySelectorAll(".carousel img").length;
const imgMaxIndex = imgCount - 1;
let currentIndex = 0;

let imgs = document.querySelectorAll(".carousel img");
let bars = document.querySelectorAll(".carousel .bar");

let timeOutId;

for (let i = 0; i < imgCount; i++) {
  bars[i].addEventListener("click", function() {
    currentIndex = i;
    showSlide();
  });
}

function showSlide() {
  clearTimeout(timeOutId);
  for (let i = 0; i < imgCount; i++) {
    bars[i].classList.remove("active");
    imgs[i].classList.remove("active");
  }
  bars[currentIndex].classList.add("active");
  imgs[currentIndex].classList.add("active");

  timeOutId = setTimeout(function() {
    swipeSlide(1);
  }, 5000);
}

function swipeSlide(num) {
  if (currentIndex + num < 0) {
    currentIndex = imgMaxIndex;
  } else if (currentIndex + num > imgMaxIndex) {
    currentIndex = 0;
  } else {
    currentIndex += num;
  }
  showSlide();
}
showSlide();