Skip to content
Advertisement

How to make a 3D carousal Inner rotation view?

I wanted to make a 3d rotating carousel like in the stack snippet, but required is a view from inside like in the image below. How can i achieve this?

window.addEventListener('load', () => {
  var carousels = document.querySelectorAll('.carousel');

  for (var i = 0; i < carousels.length; i++) {
    carousel(carousels[i]);
  }
});

function carousel(root) {
  var
    figure = root.querySelector('figure'),
    nav = root.querySelector('nav'),
    images = figure.children,
    n = images.length,
    gap = root.dataset.gap || 0,
    bfc = 'bfc' in root.dataset,
    theta = 2 * Math.PI / n,
    currImage = 0;

  setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
  
  window.addEventListener('resize', () => {
    setupCarousel(n, parseFloat(getComputedStyle(images[0]).width))
  });

  setupNavigation();

  function setupCarousel(n, s) {
    var apothem = s / (2 * Math.tan(Math.PI / n));

    figure.style.transformOrigin = `50% 50% ${- apothem}px`;

    for (var i = 0; i < n; i++) {
      images[i].style.padding = `${gap}px`;
    }
      
    for (i = 1; i < n; i++) {
      images[i].style.transformOrigin = `50% 50% ${- apothem}px`;
      images[i].style.transform = `rotateY(${i * theta}rad)`;
    }
    
    if (bfc) {
      for (i = 0; i < n; i++) {
        images[i].style.backfaceVisibility = 'hidden';
      }
    }

    rotateCarousel(currImage);
  }

  function setupNavigation() {
    nav.addEventListener('click', onClick, true);

    function onClick(e) {
      e.stopPropagation();

      var t = e.target;
      
      if (t.tagName.toUpperCase() != 'BUTTON') {
        return;
      }

      if (t.classList.contains('next')) {
        currImage++;
      } 
      else {
        currImage--;
      }

      rotateCarousel(currImage);
    }
  }

  function rotateCarousel(imageIndex) {
    figure.style.transform = `rotateY(${imageIndex * -theta}rad)`;
  }
}
body {
  margin: 0;
  font-family: 'Roboto', sans-serif;
  font-size: 16px;
}

h1 {
  text-align: center;
  margin-bottom: 1.5em;
}

h2 {
  text-align: center;
  color: #555;
  margin-bottom: 0;
}

.carousel {
  padding: 20px;
  perspective: 500px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.carousel>* {
  flex: 0 0 auto;
}

.carousel figure {
  margin: 0;
  width: 40%;
  transform-style: preserve-3d;
  transition: transform 0.5s;
}

.carousel figure img {
  width: 100%;
  box-sizing: border-box;
  padding: 0 0px;
}

.carousel figure img:not(:first-of-type) {
  position: absolute;
  left: 0;
  top: 0;
}

.carousel nav {
  display: flex;
  justify-content: center;
  margin: 20px 0 0;
}

.carousel nav button {
  flex: 0 0 auto;
  margin: 0 5px;
  cursor: pointer;
  color: #333;
  background: none;
  border: 1px solid;
  letter-spacing: 1px;
  padding: 5px 10px;
}
<h2>Eight images, with 80px gap</h2>

<div class="carousel" data-gap="80">
  <figure>
    <img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
    <img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
    <img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
    <img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
    <img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
    <img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
    <img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
    <img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
  </figure>
  
  <nav>
    <button class="nav prev">Prev</button>
    <button class="nav next">Next</button>
  </nav>
</div>

enter image description here

Advertisement

Answer

The carousel needs to be rotated and translated in the opposite direction to make it an inner rotation view.

The transform-origin sets the center of the carousel. The first two numbers are x and y, which just center the carousel on the screen. The third number is z which determines whether the carousel is moved towards the screen or away from the screen. The negative sign on apothem is removed to pull it out of the screen so the center becomes closer to the camera and achieves the inner carousel. backface-visibility needs to be always set to hidden because there might be images behind the camera now. Then to fix the rotation direction with the next button, * -theta is changed to positive.

window.addEventListener('load', () => {
  var carousels = document.querySelectorAll('.carousel');

  for (var i = 0; i < carousels.length; i++) {
    carousel(carousels[i]);
  }
});

function carousel(root) {
  var
    figure = root.querySelector('figure'),
    nav = root.querySelector('nav'),
    images = figure.children,
    n = images.length,
    gap = root.dataset.gap || 0,
    theta = 2 * Math.PI / n,
    currImage = 0;

  setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
  
  window.addEventListener('resize', () => {
    setupCarousel(n, parseFloat(getComputedStyle(images[0]).width))
  });

  setupNavigation();

  function setupCarousel(n, s) {
    var apothem = s / (2 * Math.tan(Math.PI / n));

    figure.style.transformOrigin = `50% 50% ${apothem}px`;

    for (var i = 0; i < n; i++) {
      images[i].style.padding = `${gap}px`;
    }
      
    for (i = 1; i < n; i++) {
      images[i].style.transformOrigin = `50% 50% ${apothem}px`;
      images[i].style.transform = `rotateY(${i * theta}rad)`;
    }

    rotateCarousel(currImage);
  }

  function setupNavigation() {
    nav.addEventListener('click', onClick, true);

    function onClick(e) {
      e.stopPropagation();

      var t = e.target;
      
      if (t.tagName.toUpperCase() != 'BUTTON') {
        return;
      }

      if (t.classList.contains('next')) {
        currImage++;
      } 
      else {
        currImage--;
      }

      rotateCarousel(currImage);
    }
  }

  function rotateCarousel(imageIndex) {
    figure.style.transform = `rotateY(${imageIndex * theta}rad)`;
  }
}
body {
  margin: 0;
  font-family: 'Roboto', sans-serif;
  font-size: 16px;
}

h1 {
  text-align: center;
  margin-bottom: 1.5em;
}

h2 {
  text-align: center;
  color: #555;
  margin-bottom: 0;
}

.carousel {
  padding: 20px;
  perspective: 500px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.carousel>* {
  flex: 0 0 auto;
}

.carousel figure {
  margin: 0;
  width: 40%;
  transform-style: preserve-3d;
  transition: transform 0.5s;
}

.carousel figure img {
  width: 100%;
  box-sizing: border-box;
  padding: 0 0px;
  backface-visibility: hidden;
}

.carousel figure img:not(:first-of-type) {
  position: absolute;
  left: 0;
  top: 0;
}

.carousel nav {
  display: flex;
  justify-content: center;
  margin: 20px 0 0;
}

.carousel nav button {
  flex: 0 0 auto;
  margin: 0 5px;
  cursor: pointer;
  color: #333;
  background: none;
  border: 1px solid;
  letter-spacing: 1px;
  padding: 5px 10px;
}
<h2>Eight images, with 80px gap</h2>

<div class="carousel" data-gap="80">
  <figure>
    <img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
    <img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
    <img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
    <img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
    <img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
    <img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
    <img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
    <img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
  </figure>
  
  <nav>
    <button class="nav prev">Prev</button>
    <button class="nav next">Next</button>
  </nav>
</div>
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement