DEV Community

Cover image for Infinite Circular Motion Animation in CSS and JS with Lightbox
Prahalad S
Prahalad S

Posted on

Infinite Circular Motion Animation in CSS and JS with Lightbox

Infinite Circular Motion Animation

Overview
This animation creates an infinite circular motion effect of images arranged in a carousel. The images rotate around a fixed point on the screen, simulating a circular path. Navigation buttons allow scrolling through images smoothly. Clicking an image opens it in a lightbox for enlarged viewing.

HTML Structure

  • Container: Wraps the entire carousel and controls.
  • Navigation Buttons: Two buttons (prev and next) to rotate the carousel left or right.
  • Carousel: Contains multiple .image-wrapper elements, each holding an image and a caption.
  • Top Center Text: Displays the caption of the image currently closest to the top center.
  • Lightbox: A modal overlay that shows the clicked image in larger form with a close button.
<div class="container">
    <button class="nav-button" id="prev">&#8672;</button>
    <button class="nav-button" id="next">&#8674;</button>
    <div class="carousel">
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/hand-drawn-flat-design-mountain-landscape_23-2149158786.jpg"><span>Mountain Landscape</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/hand-drawn-flat-mountain-landscape_23-2149174187.jpg"><span>Hill View</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/gradient-mountain-landscape_23-2149162007.jpg"><span>Evening Landscape</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/illustrated-blue-gradient-tropical-forest-landscape_23-2148262369.jpg"><span>Forest Landscape</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/background-with-mountain-landscape-flat-design_23-2148299136.jpg"><span>Avatar Landscape</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/elegant-beautiful-sunset-scene-background-with-palm-tree_1055-17708.jpg"><span>Sunset Background</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/seaside-white-birds-colorful-background-watercolor-vector_53876-157807.jpg"><span>White Birds</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/illustrated-landscape-with-mountains-forest_23-2148262368.jpg"><span>Mountain and River</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/hand-drawn-adventure-background_23-2149051021.jpg"><span>Adventure Background</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/flat-design-mountain-landscape_23-2149172160.jpg"><span>Sunrise Landscape</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/artistic-gradient-mountains-landscape_23-2148261005.jpg"><span>Gradient Landscape</span></div>
        <div class="image-wrapper"><img src="https://img.freepik.com/free-vector/hand-drawn-rural-landscape-background_52683-123607.jpg"><span>Rural Landscape</span></div>
    </div>
  <div id="top-center-text"></div>
</div>

<div id="lightbox">
    <span id="close">&times;</span>
    <img id="lightbox-img" src="">
</div>
Enter fullscreen mode Exit fullscreen mode

CSS Styling

  • Body uses flexbox for centering with dark background and white text.
  • Carousel and images use absolute positioning to enable circular rotation.
  • Smooth transformations (transform 0.5s ease) enable animated rotation.
  • Navigation buttons are fixed on the sides with semi-transparent backgrounds.
  • Lightbox is hidden by default and shown as a fixed overlay when active.
  • Image captions are hidden by default except for the one near the top center.
body {
  margin: 0;
  font-family: Arial, sans-serif;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: #222;
  overflow: hidden;
  color: #fff;
}
.container {
  position: relative;
  text-align: center;
}
.carousel {
  position: relative;
  width: 800px;
  height: 400px;
  margin-bottom: 20px;
}
.image-wrapper {
  position: absolute;
  top: 150px;
  left: 350px;
  width: 100px;
  height: 100px;
  transition: transform 0.5s ease;
  cursor: pointer;
}
.image-wrapper img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 10px;
}
.image-wrapper span {
  display: none;
  position: absolute;
  bottom: -25px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(0, 0, 0, 0.6);
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 0.9em;
}
.nav-button {
  display: flex;
  align-items: center;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(0, 0, 0, 0.5);
  border-radius: 10px;
  font-size: 20px;
  color: #fff;
  border: none;
  padding: 10px 15px;
  cursor: pointer;
  z-index: 10;
}
#prev {
  left: 10px;
}
#next {
  right: 10px;
}
#top-center-text {
  text-align: center;
  font-size: 1.2em;
  color: #fff;
  margin-bottom: 20px;
}
#lightbox {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.8);
  z-index: 1000;
  justify-content: center;
  align-items: center;
}
#lightbox img {
  max-width: 50%;
  max-height: 50%;
}
#close {
  position: absolute;
  top: 20px;
  right: 20px;
  color: #fff;
  font-size: 30px;
  cursor: pointer;
}

Enter fullscreen mode Exit fullscreen mode

JavaScript Functionality
Key Variables

  • radius: Radius in pixels of the circular path for the images.
  • angleStep: Degree step between each image on the circle (30 degrees).
  • currentRotation: The current rotation angle offset for the carousel.

Normalizing Rotation
A helper function ensures angles wrap between -180 and 180 degrees for proper visibility calculations updateCarousel();

This function: updateCarousel();

  • Calculates each image's angle plus current rotation.
  • Normalizes angle.
  • Sets visibility based on angle range (-90° to 90° visible).
  • Rotates each image around the circle using CSS transform: rotate + translateY for radial positioning.
  • Tracks which image is closest to the top center (angle near 0°) to update the caption displayed in the top center text.
  • Navigation Event Listeners
  • Next button decreases currentRotation by angleStep to rotate clockwise.
  • Prev button increases currentRotation by angleStep to rotate counterclockwise.
  • Both trigger updateCarousel() with smooth transform animations.

Lightbox Functionality

  • Clicking an image opens the lightbox with that image's source.
  • Clicking the close button or outside the image closes the lightbox.
const prevButton = document.getElementById("prev");
const nextButton = document.getElementById("next");
const topCenterText = document.getElementById("top-center-text");
const images = Array.from(document.querySelectorAll(".image-wrapper"));
const lightbox = document.getElementById("lightbox");
const lightboxImg = document.getElementById("lightbox-img");
const closeButton = document.getElementById("close");

const radius = 250;
const angleStep = 30;
let currentRotation = -90;

function normalizeRotation(rotation) {
  rotation = (rotation + 180) % 360;
  if (rotation < 0) rotation += 360;
  return rotation - 180;
}

function updateCarousel() {
  let closestAngle = 180;
  let topImage = null;

  images.forEach((img, index) => {
    let angle = index * angleStep + currentRotation;
    angle = normalizeRotation(angle);

    const visible = angle >= -90 && angle <= 90;
    img.style.visibility = visible ? "visible" : "hidden";
    img.style.transform = `rotate(${angle}deg) translate(0, -${radius}px)`;

    if (Math.abs(angle) < closestAngle) {
      closestAngle = Math.abs(angle);
      topImage = img;
    }
  });

  // Hide all spans
  images.forEach((img) => (img.querySelector("span").style.display = "none"));

  if (topImage) {
    const spanText = topImage.querySelector("span").textContent;
    topCenterText.textContent = spanText;
    //  topImage.querySelector('span').style.display = 'block';
  }
}

// Next button
nextButton.addEventListener("click", () => {
  currentRotation -= angleStep;
  images.forEach((img) => (img.style.transition = "transform 0.5s ease"));
  updateCarousel();
});

// Prev button
prevButton.addEventListener("click", () => {
  currentRotation += angleStep;
  images.forEach((img) => (img.style.transition = "transform 0.5s ease"));
  updateCarousel();
});

// Lightbox functionality
images.forEach((img) => {
  img.addEventListener("click", () => {
    lightbox.style.display = "flex";
    lightboxImg.src = img.querySelector("img").src;
  });
});
closeButton.addEventListener("click", () => (lightbox.style.display = "none"));
lightbox.addEventListener("click", (e) => {
  if (e.target === lightbox) lightbox.style.display = "none";
});

// Initialize
updateCarousel();

Enter fullscreen mode Exit fullscreen mode

This structure allows an infinite circular animation of images with intuitive user controls and an accessible lightbox for enhanced viewing.

Working DEMO :
Codepen Demo

Top comments (0)