CSS 滑块使用 `transform: translateX` 循环浏览图像

CSS Slider Using `transform: translateX` To Cycle Through Images

在使用 translateX 变换 属性 制作动画时,是否可以让 CSS 滑块在两个图像之间循环?

我面临几个问题:

  1. 我似乎无法显示第二张图片,即使它在 HTML 中,除非我使用 position: absolute 然后 overflow: hidden 不显示在 parent 上工作吗?

  2. 如何重置第一张图片以回到开头重新开始?

注:在动画shorthand中,动画持续2.5s,初始延迟3s。

我只想用 translateX 属性 做这个,因为我想要 60FPS 的平滑度(完成后会用 translate3d 做,但为了让代码更简单阅读我使用过 translateX)。我不想为 margin: leftleft 属性 等设置动画

任何帮助都会很棒。

代码在下方或 link 到 Codepen:https://codepen.io/anna_paul/pen/ZEJrvRp

body {
  position: relative;
  margin: 0;
  display: flex;
  justify-content: center;
}

.container {
  width: 500px;
  height: 333px;
  overflow: hidden;
}

.slider-wrapper {
  display: flex;
}

.image {
  display: block;
}

.hero-image-1 {
  transform: translateX(0);
  animation: slide-out-image-1 2.5s 3s cubic-bezier(0.54, 0.12, 0.44, 1)
    forwards;
}

@keyframes slide-out-image-1 {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-100%);
  }
}

.hero-image-2 {
  transform: translateX(100%);
  animation: slide-in-image-2 2.5s 3s cubic-bezier(0.54, 0.12, 0.44, 1) forwards;
}

@keyframes slide-in-image-2 {
  0% {
    transform: translateX(100%);
  }
  100% {
    transform: translateX(0);
  }
}
<div class="container">
  <div class="slider-wrapper">
    <picture>
      <img class="image hero-image-1" src="https://drive.google.com/uc?export=view&id=1l7cTX35wqd-4eYvFL8A5QLZ7LbOF9m4J">
    </picture>
    <picture>
      <img class="image hero-image-2" src="https://drive.google.com/uc?export=view&id=1iB9R1aoeYSmkPX9Ju3NNOZhKylOjCA0y">
    </picture>
  </div>
</div>

不,没有 position:absolute 这是不可能的。

对于位置重置,您可以使用 Javascript。这是一个例子;

var counter = 1;
setInterval(function()
{
  document.getElementById('radio' + counter).checked=true;
  counter++;
  if(counter>4){
    counter=1;
  }
}, 5000);

你可以给.container一个position:relative来使溢出生效。绝对定位的元素要求它的容器是 a positioned element.

body {
  position: relative;
  margin: 0;
  display: flex;
  justify-content: center;
}

.container {
  width: 500px;
  height: 333px;
  overflow: hidden;
  position:relative;
}

.slider-wrapper {
  display: flex;
}

.image {
  position:absolute;
  display: block;
}

.hero-image-1 {
  transform: translateX(0);
  animation: slide-out-image-1 2.5s 3s cubic-bezier(0.54, 0.12, 0.44, 1)
    forwards;
}

@keyframes slide-out-image-1 {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-100%);
  }
}

.hero-image-2 {
  transform: translateX(100%);
  animation: slide-in-image-2 2.5s 3s cubic-bezier(0.54, 0.12, 0.44, 1) forwards;
}

@keyframes slide-in-image-2 {
  0% {
    transform: translateX(100%);
  }
  100% {
    transform: translateX(0);
  }
}
<div class="container">
  <div class="slider-wrapper">
    <picture>
      <img class="image hero-image-1" src="https://drive.google.com/uc?export=view&id=1l7cTX35wqd-4eYvFL8A5QLZ7LbOF9m4J">
    </picture>
    <picture>
      <img class="image hero-image-2" src="https://drive.google.com/uc?export=view&id=1iB9R1aoeYSmkPX9Ju3NNOZhKylOjCA0y">
    </picture>
  </div>
</div>

要创建滑块循环,您需要在滑块移动和停止时定义动画点。在你的情况下,看起来


2.5s + 3s +2.5s +3s = 11s or move + pause(out of container) + move + pause = total.


对于第一个图像,我们需要在容器外设置动画,然后移动到起始位置(在容器外),等待(第二个图像设置动画),然后设置到初始位置并暂停。

对于第二张图片,我们只显示然后隐藏。

body {
  height: 100vh;
  background-color: hsl(201, 27%, 10%);
  color: white;
  display: grid;
  place-items: center;
  position: relative;
}

:root {
  --animate: 2.5s;
  --pause: 3s;
  --totall-duration: calc(var(--animate) * 2 + var(--pause) * 2);
}

.container {
  width: 500px;
  height: 333px;
  overflow: hidden;
}

.slider-wrapper {
  width: 200%;
  display: flex;
  position: relative;
}

.image {
  display: flex;
  width: 100%;
  height: 100%;
}

.hero-image-1 {
  top: 0;
  left: 0;
  position: absolute;
  animation: slide-out-image-1 var(--totall-duration) 3s
    cubic-bezier(0.54, 0.12, 0.44, 1) infinite;
}
/* start + pause + start + pause = totall */
/* 2.5s + 3s + 2.5s + 3s = 11s*/
@keyframes slide-out-image-1 {
  0% {
    transform: translateX(0);
  }
  25% {
    transform: translateX(-100%);
  }
  25.0001% {
    transform: translateX(100%);
  }
  50% {
    transform: translateX(100%);
  }
  75% {
    transform: translateX(0%);
  }
  100% {
    transform: translateX(0%);
  }
}

.hero-image-2 {
  top: 0;
  right: 0;
  position: absolute;
  animation: slide-in-image-2 var(--totall-duration) 3s
    cubic-bezier(0.54, 0.12, 0.44, 1) infinite;
}

/* start + pause + start + pause = totall */
/* 2.5s + 3s + 2.5s + 3s = 11s*/
@keyframes slide-in-image-2 {
  0% {
    transform: translateX(0);
  }
  25% {
    transform: translateX(-100%);
  }
  50% {
    transform: translateX(-100%);
  }
  75% {
    transform: translateX(-200%);
  }
  100% {
    transform: translateX(-200%);
  }
}
<div class="container">
  <div class="slider-wrapper">
    <picture class="hero-image-1">
      <img class="image" src="https://drive.google.com/uc?export=view&id=1l7cTX35wqd-4eYvFL8A5QLZ7LbOF9m4J" />
    </picture>
    <picture class="hero-image-2">
      <img class="image" src="https://drive.google.com/uc?export=view&id=1iB9R1aoeYSmkPX9Ju3NNOZhKylOjCA0y" />
    </picture>
  </div>
</div>

我们可以使用 background 属性.

来做滑块

<!DOCTYPE html>
<html>
  <head>
    <style>
      body { background-color: wheat; }
      h1 {
        text-align: center;
        color: tomato;
      }
      .slider {
        width: 60vw;
        margin: 0 auto;
        aspect-ratio: 485/323;

        transform: translate3d(0, 0, 0);
        background-image: url("https://drive.google.com/uc?export=view&id=1l7cTX35wqd-4eYvFL8A5QLZ7LbOF9m4J"),
          url("https://drive.google.com/uc?export=view&id=1iB9R1aoeYSmkPX9Ju3NNOZhKylOjCA0y");
        background-attachment: scroll, scroll;
        background-position: 0% 50%, 100% 50%;
        background-repeat: no-repeat, no-repeat;
        background-size: 100%, 100%;

        animation: slide-background 11s cubic-bezier(0.50, 0.0, 0.50, 1) 3s infinite;
      }
      @keyframes slide-background {
        0% { background-position: 0% 50%, 60vw 50%; }
        25% { background-position: -60vw 50%, 0% 50%; }
        25.01% { background-position: 60vw 50%, 0% 50%; }
        50% { background-position: 60vw 50%, 0% 50%; }
        75% { background-position: 0% 50%, -60vw 50%; }
        100% { background-position: 0% 50%, 60vw 50%; }
      }
    </style>
  </head>

  <body>
    <div class="slider"><h1>Cars!</h1></div>
  </body>
</html>

如果您更改浏览器大小,它会调整大小。

我知道您想使用 translate 来提高性能。 不知道 transform: translate3d(0, 0, 0); 技巧在此处对 background-* 属性设置动画有多大作用。它是否获得相同的硬件加速?

我成功地创建了一个可扩展的 CSS 轮播。它基于 Christian Schaefer 的想法,即使用 scroll snap with shift。

以下是 5 幻灯片轮播。为了使其正常工作,您需要确保:

  • 您将第一个 .carousel-slide 元素的副本放在列表末尾。
  • 设置变量:
    • --无幻灯片:6; //包括重复的第一张幻灯片= 5+1
    • --幻灯片时间:4s; // 停留时间75% + 滑动时间25%
  • 修改tostart关键帧规则,根据相应的计算设置时间。

     /* variables */
      :root {
        --no-slides: 6;  /* including duplicated first slide */
        --slide-time: 4s; /* stay time 75% + transition time 25% */
      }


      /* document styles */
      body {
        max-width: 400px;
        margin: 0 auto;
      }

      * {
        box-sizing: border-box;
        scrollbar-color: transparent transparent;
        scrollbar-width: 0px;
      }

      *::-webkit-scrollbar {
        width: 0;
      }

      *::-webkit-scrollbar-track {
        background: transparent;
      }

      *::-webkit-scrollbar-thumb {
        background: transparent;
        border: none;
      }

      * {
        -ms-overflow-style: none;
      }

      
      /* carousel styles */
      .carousel {
        position: relative;
        padding-top: 75%;
        perspective: 100px;
      }

      .carousel-viewport {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        display: flex;
        overflow-x: scroll;
        scroll-behavior: smooth;
        scroll-snap-type: x mandatory;
      }

      .carousel-viewport,
      .carousel-slide {
        list-style: none;
        margin: 0;
        padding: 0;
      }

      .carousel-slide {
        position: relative;
        flex: 0 0 100%;
        width: 100%;
        background: linear-gradient(90deg,#ff4e50, #f9d423);
      }

      

      .carousel-pusher {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        scroll-snap-align: center;
      }

      /* animations */
      @media (hover: hover) {
        .carousel-pusher {
          animation-delay: 0s;
          animation-name: tonext, snap;
          animation-timing-function: ease;
          animation-duration: var(--slide-time);
          animation-iteration-count: infinite;
        }

        .carousel-slide:last-child .carousel-pusher {
          animation-name: tostart, snap;
          animation-duration: calc(var(--slide-time) * (var(--no-slides) - 1)), var(--slide-time);
          animation-delay: var(--slide-time), 0s;
        }
      }

      @keyframes tonext {
        0% {
          filter: opacity(0);
        }
        75% {
          transform: translateX(0);
        }
        95% {
          transform: translateX(100%);
        }
        98% {
          transform: translateX(100%);
        }
        99% {
          filter: opacity(1);
          transform: translateX(0%);
        }
      }

      @keyframes snap {
        96% {
          scroll-snap-align: center;
        }
        97% {
          scroll-snap-align: none;
        }
        99% {
          scroll-snap-align: none;
        }
        100% {
          scroll-snap-align: center;
        }
      }

      @keyframes tostart {
        95.8% {     /* some fractions less than next frame time */
          transform: translateX(0%);
        }
        95.823% { /* 0.75 * (100%/--no-slides) + 100% - (100%/--no-slides) +-0.500 adjustment*/
      transform: translateX(calc((var(--no-slides)) * -100%));
    }
    99.5% {  /* 0.99 * (100%/--no-slides) + 100% - (100%/--no-slides)  +-0.500 adjustment*/
      transform: translateX(calc((var(--no-slides) - 2) * -100%));
    }
      }


      /* slide content styles */
      .content {
        padding: 20px ;
        font-size: 150px;
        text-align: center;
      }
<div class="carousel">
      <ul class="carousel-viewport">

        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content">1 &#128522;</div>
        </li>

        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content">2 &#128521;</div>
        </li>

        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content">3 &#128516</div>
        </li>

        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content">4 &#128580;</div>
        </li>


        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content">5 &#128526;</div>
        </li>

        <!-- repeat first slide -->
        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content">1 &#128522;</div>
        </li>
      </ul>
    </div>

要获得正确和平滑的过渡,请修改 tostart 关键帧更改时间或添加更多帧。

注意:如果动画停止,您必须将轮播设置为第一张幻灯片,然后再开始动画。同步了三个动画运行。


2个幻灯片版本

<!DOCTYPE html>
<html>
  <head>
    <style>
      /* variables */
      :root {
        --no-slides: 3; /* including duplicated first slide */
        --slide-time: 4s; /* stay time 75% + transition time 25% */
      }

      /* document styles */
      body {
        max-width: 400px;
        margin: 0 auto;
      }

      * {
        box-sizing: border-box;
        scrollbar-color: transparent transparent;
        scrollbar-width: 0px;
      }

      *::-webkit-scrollbar {
        width: 0;
      }

      *::-webkit-scrollbar-track {
        background: transparent;
      }

      *::-webkit-scrollbar-thumb {
        background: transparent;
        border: none;
      }

      * {
        -ms-overflow-style: none;
      }

      /* carousel styles */
      .carousel {
        position: relative;
        padding-top: 75%;
        perspective: 100px;
      }

      .carousel-viewport {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        display: flex;
        overflow-x: scroll;
        scroll-behavior: smooth;
        scroll-snap-type: x mandatory;
      }

      .carousel-viewport,
      .carousel-slide {
        list-style: none;
        margin: 0;
        padding: 0;
      }

      .carousel-slide {
        position: relative;
        flex: 0 0 100%;
        width: 100%;
        background-color: #f995;
      }

      .carousel-slide:nth-child(odd) {
        background-color: cadetblue;
      }
      .carousel-pusher {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        scroll-snap-align: center;
      }

      /* animations */
      @media (hover: hover) {
        .carousel-pusher {
          animation-delay: 0s;
          animation-name: tonext, snap;
          animation-timing-function: ease;
          animation-duration: var(--slide-time);
          animation-iteration-count: infinite;
        }

        .carousel-slide:last-child .carousel-pusher {
          animation-name: tostart, snap;
          animation-duration: calc(var(--slide-time) * (var(--no-slides) - 1)),
            var(--slide-time);
          animation-delay: var(--slide-time), 0s;
        }
      }

      @keyframes tonext {
        0% {
          filter: opacity(0);
        }
        75% {
          transform: translateX(0);
        }
        95% {
          transform: translateX(100%);
        }
        98% {
          transform: translateX(100%);
        }
        99% {
          filter: opacity(1);
          transform: translateX(0%);
        }
      }

      @keyframes snap {
        96% {
          scroll-snap-align: center;
        }
        97% {
          scroll-snap-align: none;
        }
        99% {
          scroll-snap-align: none;
        }
        100% {
          scroll-snap-align: center;
        }
      }

      @keyframes tostart {
        74.999% {
          /* some fractions less than next frame */
          transform: translateX(0%);
        }
        75% {
          /* 0.75 * (100%/--no-slides) + 100% - (100%/--no-slides) */
          transform: translateX(calc((var(--no-slides)) * -100%));
        }
        99.3% {
          /* 0.99 * (100%/--no-slides) + 100% - (100%/--no-slides)  -  <1.0 - 0.0 adjustment>*/
          transform: translateX(calc((var(--no-slides) - 2) * -100%));
        }
      }

      /* slide content styles */
      .content {
        object-fit: fill;
      }
      .image {
        padding: 0px;
        margin: 0px;
        width: 100%;
        height: 100%;
        object-fit: fill;
      }
    </style>
  </head>

  <body>
    <div class="carousel">
      <ul class="carousel-viewport">
        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content"
            ><img
              class="image"
              src="https://drive.google.com/uc?export=view&id=1l7cTX35wqd-4eYvFL8A5QLZ7LbOF9m4J"
          /></div>
        </li>

        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content"
            ><img
              class="image"
              src="https://drive.google.com/uc?export=view&id=1iB9R1aoeYSmkPX9Ju3NNOZhKylOjCA0y"
          /></div>
        </li>

        <!-- repeat first slide -->
        <li class="carousel-slide">
          <div class="carousel-pusher"></div>
          <div class="content"
            ><img
              class="image"
              src="https://drive.google.com/uc?export=view&id=1l7cTX35wqd-4eYvFL8A5QLZ7LbOF9m4J"
          /></div>
        </li>
      </ul>
    </div>
  </body>
</html>