I'm trying to create a carousel which automatically plays in a loop. Initially I was in the process of implementing slick slider, but then I came across this CSS only approach:
body { align-items: center; background: #E3E3E3; display: flex; height: 100vh; justify-content: center;
}
@keyframes scroll { 0% { transform: translateX(0); } 100% { transform: translateX(calc(-250px * 7)); }
}
.slider { background: white; box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.125); height: 100px; margin: auto; overflow: hidden; position: relative; width: 960px;
}
.slider::before,
.slider::after { background: linear-gradient(to right, white 0%, rgba(255, 255, 255, 0) 100%); content: ""; height: 100px; position: absolute; width: 200px; z-index: 2;
}
.slider::after { right: 0; top: 0; transform: rotateZ(180deg);
}
.slider::before { left: 0; top: 0;
}
.slider .slide-track { animation: scroll 40s linear infinite; display: flex; width: calc(250px * 14);
}
.slider .slide { height: 100px; width: 250px;
}<div> <div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> </div>
</div>Now, I'm trying to reverse engineer and implement something similar for my carousel:
@keyframes scroll { 0% { transform: translateX(0); } 100% { transform: translateX(calc(-250px * 7)); }
}
.carousel { padding: 100px 0; background: lightblue; overflow: hidden; position: relative;
}
.carousel__wrapper { display: flex; justify-content: center; align-items: center;
}
.carousel__slide { animation: scroll 10s linear infinite; display: flex; flex-direction: column; width: 33%;
}
.carousel__image { background-size: cover; background-repeat: no-repeat; height: 100px; width: 100px; margin: 15px 20px;
}<link href="" rel="stylesheet">
<section> <div> <div> <div> <div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> </div> </div> </div> </div>
</section>In my demo, at the end of the carousel there's blank space. Whereas in the working demo above, the logos loop smoothly (starts from the beginning without the blank space).
Now, in the working demo above, I stripped back all the CSS to only this:
@keyframes scroll { 0% { transform: translateX(0); } 100% { transform: translateX(calc(-250px * 7)); }
}
.slide-track { animation: scroll 10s linear infinite; display: flex;
}<div> <div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> <div> <img src="" height="100" width="250" alt="" /> </div> </div>
</div>The above is working, and the css from above exists on my demo, but it doesn't work as smoothly? How can I make the transition (make it start from the beginning) after the last slide is shown?
1 Answer
The carousel works based on fixed widths. If there are 7 slides:
- They've duplicated slides once in the html. Make sure you repeat slides.
- The
carousel__wrapperhas givenwidth: calc(250px * 14);. Twice the number of slides to show. Note, the wrapper is dependent on slide width and slides are not dependent on wrapper. - The
carouselhas width less than the wrapper it's250px * 4and the overflow is hidden so we see only the window and not entire carousel__wrapper. - The animation shifts the slides by
calc(-250px * 7)to left. Here, 250px is slide width. Note they are shifting only by 7 slides not entire 14.
You can't use relative dimensions without using javascript. To keep it CSS only you'll need absolute widths.
If you use variables then things will be easy to maintain and understand:
:root { --no-of-slides: 6; --slides-in-view: 4; --slide-width: 200px; --slide-height: 300px; --iteration-time: 10s;
}
@keyframes scroll { 0% { transform: translateX(0); } 100% { transform: translateX(calc(var(--slide-width) * var(--no-of-slides)* -1)); }
}
.carousel__wrapper { display: flex; /*justify-content: center;*/ align-items: center; width: calc(var(--slides-in-view) * var(--slide-width)); overflow: hidden; border: 1px dashed gray; margin: 0 auto;
}
.carousel { padding: 100px 0; background: lightblue; overflow: hidden; width: calc(2 * var(--no-of-slides));
}
.carousel__slide { animation: scroll var(--iteration-time) linear infinite; display: flex; flex-direction: column; flex: 0 0 auto; width: var(--slide-width); height: var(--slide-height); box-sizing: border-box; /*border: 1px dotted darkblue;*/
}
.carousel__image { background-size: cover; background-repeat: no-repeat; height: 50%; /*width: 100px;*/ margin: 15px 20px;
}
/* just for analysis remove this 3 rules later*/ .carousel__slide { position: relative; } .carousel { counter-reset: slideNo; } .carousel__slide::before { counter-increment: slideNo; content: counter(slideNo); position: absolute; top: 0%; left: 50%; font-size: 2rem; color: lime; }<link href="" rel="stylesheet">
<section> <div> <div> <div> <div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div> </div> <div> </div> </div> <!--#### repeat ####--> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div> </div> <div> </div> </div> </div> </div> </div> </div>
</section>In above CSS rules my changes are after blank lines. See the comments in the code.
If you want dimensions based on viewport then you can use vw and vh units:
:root { --no-of-slides: 6; --slides-in-view: 4; --slide-width: 33vw; --slide-height: 50vh; --iteration-time: 10s;
}
@keyframes scroll { 0% { transform: translateX(0); } 100% { transform: translateX(calc(var(--slide-width) * var(--no-of-slides)* -1)); }
}
.carousel__wrapper { display: flex; /*justify-content: center;*/ align-items: center; width: calc(var(--slides-in-view) * var(--slide-width)); overflow: hidden; border: 1px dashed gray; margin: 0 auto;
}
.carousel { padding: 10px 0; background: lightblue; overflow: hidden; width: calc(2 * var(--no-of-slides));
}
.carousel__slide { animation: scroll var(--iteration-time) linear infinite; display: flex; flex-direction: column; flex: 0 0 auto; width: var(--slide-width); height: var(--slide-height); box-sizing: border-box; /*border: 1px dotted darkblue;*/
}
.carousel__image { background-size: cover; background-repeat: no-repeat; height: 50%; /*width: 100px;*/ margin: 15px 20px;
}
/* just for analysis remove this 3 rules later*/ .carousel__slide { position: relative; } .carousel { counter-reset: slideNo; } .carousel__slide::before { counter-increment: slideNo; content: counter(slideNo); position: absolute; top: 0%; left: 50%; font-size: 2rem; color: lime; }<link href="" rel="stylesheet">
<section> <div> <div> <div> <div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div> </div> <div> </div> </div> <!--#### repeat ####--> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div></div> <div></div> </div> <div> <div> </div> <div> </div> </div> </div> </div> </div> </div>
</section> 1