无限滚动轮播(仅限 CSS)

Infinite scrolling carousel (CSS only)

我正在尝试创建一个自动循环播放的轮播。最初我正在实施 slick slider,但后来我遇到了这种 CSS 的方法:

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 class="slider">
  <div class="slide-track">
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/1.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/2.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/3.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/4.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/5.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/6.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/7.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/1.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/2.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/3.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/4.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/5.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/6.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/7.png" height="100" width="250" alt="" />
    </div>
  </div>
</div>

现在,我正在尝试对我的旋转木马进行逆向工程和实现类似的东西:

@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="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">

<section class="carousel">

  <div class="container-fluid px-0">
    <div class="row">
      <div class="col-12">
        <div class="carousel__wrapper">

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

        </div>
      </div>
    </div>
  </div>

</section>

在我的演示中,在 carousel 的末尾有空白 space。而在上面的工作演示中,徽标流畅地循环(从头开始没有空白 space)。

现在,在上面的工作演示中,我将所有 CSS 剥离为仅此:

@keyframes scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(calc(-250px * 7));
  }
}

.slide-track {
  animation: scroll 10s linear infinite;
  display: flex;
}
<div class="slider">
  <div class="slide-track">
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/1.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/2.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/3.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/4.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/5.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/6.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/7.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/1.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/2.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/3.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/4.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/5.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/6.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/7.png" height="100" width="250" alt="" />
    </div>
  </div>
</div>

上面的是有效的,上面的 css 存在于我的演示中,但它运行得不太顺利?如何在显示最后一张幻灯片后进行过渡(使其从头开始)?

轮播基于固定宽度工作。如果有7张幻灯片:

  1. 他们在 html 中复制了一次幻灯片。确保重复幻灯片。
  2. carousel__wrapper 已给出 width: calc(250px * 14);。要显示的幻灯片数量的两倍。请注意,包装器取决于幻灯片宽度,幻灯片不依赖于包装器。
  3. carousel 的宽度小于 250px * 4 的包装器,溢出部分被隐藏,因此我们只能看到 window 而不是整个 carousel__wrapper.
  4. 动画将幻灯片向左移动 calc(-250px * 7)。这里,250px 是幻灯片宽度。请注意,它们仅移动了 7 张幻灯片,而不是整个 14 张幻灯片。

不使用 javascript 就不能使用相对尺寸。要保持它 CSS 只有你需要绝对宽度。
如果您使用变量,那么事情将易于维护和理解:

: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="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">

<section class="carousel">

  <div class="container-fluid px-0">
    <div class="row">
      <div class="col-12">
        <div class="carousel__wrapper">

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
          </div>

          <!--#### repeat ####-->
          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

在上面的 CSS 规则中,我的更改是在空行之后。查看代码中的注释。


如果您想要基于视口的尺寸,那么您可以使用 vwvh 单位:

: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="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">

<section class="carousel">

  <div class="container-fluid px-0">
    <div class="row">
      <div class="col-12">
        <div class="carousel__wrapper">

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
          </div>

          <!--#### repeat ####-->
          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>