Search code examples
htmlcsscss-transitions

Css carousel infinite scroll without jerking when loop


I have this code of the carousel. issue is that when it reaches the end of the loop it jerks and then starts. is there way that we can loop start without noticing? here is my code. i don't want to use js. i there a way we can do that? i don't want to make changes when screen size changes. just want .marquee-item to be at 580px.

.marquee-wrapper{
  background:#2F394C;
  text-align:center;
}
.marquee-wrapper .container{
  overflow:hidden;
}
.marquee-inner span{
  display:flex;
  gap:40px;
}
.marquee-wrapper .marquee-block{
  --total-marquee-items:5;
  height: 150px;
  width: calc(30% * (var(--total-marquee-items)));
  overflow: hidden;
  box-sizing: border-box;
  position: relative;
  margin: 20px auto;
  background:#1B2531;
  padding: 30px 0;
}
.marquee-inner{
  display: flex;
  width: 200%;
  position: absolute;
}
.marquee-item{
  width: 580px;
  height: auto;
  float: left;
  transition: all .2s ease-out;
  background:#00cc00;
}
.marquee-item:last-child {
  margin: 0 50px 0 20px;
}
.marquee-inner p{
  font-weight: 800;
  font-size: 30px;
  font-family: cursive;
}
.marquee-inner.to-left{
  animation: marqueeLeft 5s linear infinite;
}
.marquee-inner.to-right{
  animation: marqueeRight 5s linear infinite;
}
@keyframes marqueeLeft{
  0% {
    left: 0;
  }
  100% {
    left: -100%;
  }
}
@keyframes marqueeRight{
  0% { 
    left: -100%; 
  }
  100% {
   left: 0; 
  }
}
<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>CSS Carousel</title>

</head>
<body>

<div class="marquee-wrapper">
    <div class="container">
        <div class="marquee-block">
            <div class="marquee-inner to-left">
                <span>
                    <div class="marquee-item">
                        <p class="text-white">1</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">2</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">3</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">4</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">5</p>
                    </div>
                </span>
                <span>
                    <div class="marquee-item">
                        <p class="text-white">1</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">2</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">3</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">4</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">5</p>
                    </div>
                </span>
            </div>
        </div>
        <div class="marquee-block">
            <div class="marquee-inner to-right">
                <span>
                    <div class="marquee-item">
                        <p class="text-white">5</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">4</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">3</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">2</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">1</p>
                    </div>
                </span>
                <span>
                    <div class="marquee-item">
                        <p class="text-white">5</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">4</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">3</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">2</p>
                    </div>
                    <div class="marquee-item">
                        <p class="text-white">1</p>
                    </div>
                </span>
            </div>
        </div>
    </div>
</div>
  
</body>
</html>


Solution

  • There had 2 major problems:

    • The width you set on .marquee-inner was in percentage, which reflects the percentage of the parent. Here, since the parent was forced a smaller size than the content, this was not good. I instead replaced it with a formula to take the width of each element, plus their spacing and multiply it by the amount of elements.
    • The animation was using position:absolute in pair with left:-100% again, percentages use the parents sizes. In this example you were translating left by 100% of the size of the container while you wanted 100% of the size of the content. I have used a transform instead for 2 reasons: performance (always better to transform than actually change the position), and also because in it, percentages are referring to the actual element so -100% would work as you'd expect.

    Now, you will see a little flickering when the animation loops. This happens because the first and last frame of the animation are exactly the same, so you see it as if the element stops for a short bit. To circumvent this you'd have to substract the % traveled in one frame to the 100%. There are certainly techniques to circumvent that but I am not aware of them.

    .marquee-wrapper {
      background: #2F394C;
      text-align: center;
    }
    
    .marquee-wrapper .container {
      overflow: hidden;
    }
    
    .marquee-inner span {
      display: flex;
      gap: 40px;
    }
    
    .marquee-wrapper .marquee-block {
      --total-marquee-items: 5;
      height: 150px;
      width: calc(30% * (var(--total-marquee-items)));
      overflow: hidden;
      box-sizing: border-box;
      position: relative;
      margin: 20px auto;
      background: #1B2531;
      padding: 30px 0;
    }
    
    .marquee-inner {
      display: flex;
      width: calc((580px + 40px) *( var(--total-marquee-items)) + 40px);
      position: absolute;
    }
    
    .marquee-item {
      width: 580px;
      height: auto;
      float: left;
      transition: all .2s ease-out;
      background: #00cc00;
    }
    
    .marquee-item:last-child {
      margin: 0 50px 0 20px;
    }
    
    .marquee-inner p {
      font-weight: 800;
      font-size: 30px;
      font-family: cursive;
    }
    
    .marquee-inner.to-left {
      animation: marqueeLeft 5s linear infinite;
    }
    
    .marquee-inner.to-right {
      animation: marqueeRight 5s linear infinite;
    }
    
    @keyframes marqueeLeft {
      0% {
        transform: translateX(0);
      }
      100% {
        transform: translateX(-100%);
      }
    }
    
    @keyframes marqueeRight {
      0% {
        transform: translateX(-100%);
      }
      100% {
        transform: translateX(0);
      }
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>CSS Carousel</title>
    
    </head>
    
    <body>
    
      <div class="marquee-wrapper">
        <div class="container">
          <div class="marquee-block">
            <div class="marquee-inner to-left">
              <span>
                        <div class="marquee-item">
                            <p class="text-white">1</p>
                        </div>
                        <div class="marquee-item">
                            <p class="text-white">2</p>
                        </div>
                        <div class="marquee-item">
                            <p class="text-white">3</p>
                        </div>
                        <div class="marquee-item">
                            <p class="text-white">4</p>
                        </div>
                        <div class="marquee-item">
                            <p class="text-white">5</p>
                        </div>
                    </span>
              <span>
                        <div class="marquee-item">
                            <p class="text-white">1</p>
                        </div>
                        <div class="marquee-item">
                            <p class="text-white">2</p>
                        </div>
                        <div class="marquee-item">
                            <p class="text-white">3</p>
                        </div>
                        <div class="marquee-item">
                            <p class="text-white">4</p>
                        </div>
                        <div class="marquee-item">
                            <p class="text-white">5</p>
                        </div>
                    </span>
            </div>
          </div>