Search code examples
javascriptsliderimage-rotationtranslate-animation

How to first rotate an image and then translate it in an image slider


I am trying to build an image slider in which when pressing the previous or next button my image slider first rotates 180deg then translates the width of the image to show next/previous image.

I can't get it to work and I think that I may need a solution using an async/await function which I am really not very good at right now. I tried to write an asynchronus function but could not get it right.

Maybe someone can show me the way to solve this?

const container = document.querySelectorAll('.image-container')
const prevBtn = document.querySelector('.prev')
let counter = 0

prevBtn.addEventListener('click', moveSlide)

function moveSlide() {
  counter++
  container.forEach((image, index) => {
    let width = image.getBoundingClientRect().width
    image.style.transform = `rotateY(180deg)`
    setTimeout(() => {
      image.style.transform = `translate(-${counter*width}px`
    }, 450)

  })
}
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

body {
  display: flex;
  height: 100vh;
  overflow: hidden;
  align-items: center;
  align-content: center;
  justify-content: center;
}

.wrapper {
  display: flex;
  flex-direction: column;
}

.slider-container {
  height: 50vh;
  width: 300px;
  display: flex;
  margin: auto;
  flex-direction: row;
  overflow: hidden;
}

.image-container {
  display: block;
  width: 300px;
  transition: transform 450ms ease;
}

.btn-container {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: row;
  gap: 5px;
}

.btn-container .btn {
  width: 15vw;
  padding: 5px;
}
<div class="wrapper">
  <div class="slider-container">
    <div class="image-container">
      <img src="https://picsum.photos/id/237/300/200" alt="">
    </div>
    <div class="image-container">
      <img src="https://picsum.photos/seed/picsum/300/200" alt="">
    </div>
    <div class="image-container">
      <img src="https://picsum.photos/300/200?grayscale" alt="">
    </div>
    <div class="image-container">
      <img src="https://picsum.photos/300/200/?blur" alt="">
    </div>
    <div class="image-container">
      <img src="https://picsum.photos/id/870/300/200?grayscale&blur=2" alt="">
    </div>
    <div class="image-container">
      <img src="https://picsum.photos/id/1/300/200" alt="">
    </div>
  </div>
  <div class="btn-container">
    <button class="btn prev">Previous</button>
    <button class="btn next">Next</button>
  </div>
</div>


Solution

  • There were 2 issues. In your code you rotated all the containers of the images at the same time. This caused them all to shrink to the left. Other issue - you needn't rotate the container, but rather the img within it. So I added the transition property to the .image-container img and made sure to rotate the image.

    Also, it is better to use events instead of timeout to detect when the transition ends. I didn't go there. Also, consider using a library for this common task.

    const container = document.querySelectorAll('.image-container')
    const prevBtn = document.querySelector('.prev')
    let counter = 0
    
    prevBtn.addEventListener('click', moveSlide)
    
    function moveSlide() {
      counter++
    
      image = document.querySelector(".image-container:nth-child(" + counter + ") img")
      image.style.transform = `rotateY(180deg)`
      image.style.border = "2px solid red"
    
      container.forEach((image, index) => {
        let width = image.getBoundingClientRect().width
    
        setTimeout(() => {
          image.style.transform = `translate(-${counter*width}px`
        }, 450)
    
      })
    }
    * {
      padding: 0;
      margin: 0;
      box-sizing: border-box;
    }
    
    body {
      display: flex;
      height: 100vh;
      overflow: hidden;
      align-items: center;
      align-content: center;
      justify-content: center;
    }
    
    .wrapper {
      display: flex;
      flex-direction: column;
    }
    
    .slider-container {
      height: 50vh;
      width: 300px;
      display: flex;
      margin: auto;
      flex-direction: row;
      overflow: hidden;
    }
    
    .image-container,
    .image-container img {
      display: block;
      width: 300px;
      transition: transform 450ms ease;
    }
    
    .btn-container {
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: row;
      gap: 5px;
    }
    
    .btn-container .btn {
      width: 15vw;
      padding: 5px;
    }
    <div class="wrapper">
      <div class="slider-container">
        <div class="image-container">
          <img src="https://picsum.photos/id/237/300/200" alt="">
        </div>
        <div class="image-container">
          <img src="https://picsum.photos/seed/picsum/300/200" alt="">
        </div>
        <div class="image-container">
          <img src="https://picsum.photos/300/200?grayscale" alt="">
        </div>
        <div class="image-container">
          <img src="https://picsum.photos/300/200/?blur" alt="">
        </div>
        <div class="image-container">
          <img src="https://picsum.photos/id/870/300/200?grayscale&blur=2" alt="">
        </div>
        <div class="image-container">
          <img src="https://picsum.photos/id/1/300/200" alt="">
        </div>
      </div>
      <div class="btn-container">
        <button class="btn prev">Previous</button>
        <button class="btn next">Next</button>
      </div>
    </div>