CSS无限和圆形旋转图像滑块

源节点: 1765846

图像滑块(也称为轮播)无处不在。 有 很多 CSS 技巧来创建通用滑块 图像从左向右(或相反)滑动的位置。 这是同样的处理 那里有许多 JavaScript 库 创建带有复杂动画的精美滑块。 我们不会在这篇文章中做任何事情。

通过一系列小文章,我们将探索一些奇特且不常见的纯 CSS 滑块。 如果您厌倦了看到相同的经典滑块,那么您来对地方了!

CSS 滑块系列

对于第一篇文章,我们将从我称之为“圆形旋转图像滑块”的东西开始:

酷吧? 让我们剖析代码!

HTML 标记

如果你关注了我的系列 花哨的图像装饰 or CSS 网格和自定义形状,那么您就知道我的第一条规则是尽可能使用最小的 HTML。 在让我的代码变得混乱之前,我总是努力寻找 CSS 解决方案

s 和其他东西。

同样的规则也适用于此——我们的代码只不过是容器中的图像列表。

假设我们正在处理四张图片:

而已! 现在让我们转到代码中有趣的部分。 但首先,我们将深入了解滑块的工作逻辑。

我们如何运作?

这是我删除的视频 overflow: hidden 从 CSS 中我们可以更好地理解图像是如何移动的:

就好像我们的四张图片放在一个逆时针旋转的大圆圈上。

所有图像具有相同的大小(表示为 S 图中)。 请注意蓝色圆圈,它是与所有图像的中心相交且半径为(R). 稍后我们的动画将需要这个值。 R 等于 0.707 * S. (我将跳过给出该方程式的几何学。)

让我们写一些 CSS!

我们将使用 CSS网格 将所有图像放在彼此上方的同一区域中:

.gallery  {
  --s: 280px; /* control the size */

  display: grid;
  width: var(--s);
  aspect-ratio: 1;
  padding: calc(var(--s) / 20); /* we will see the utility of this later */
  border-radius: 50%;
}
.gallery > img {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

到目前为止没有什么太复杂的。 棘手的部分是动画。

我们谈到旋转一个大圆圈,但实际上,我们将单独旋转每个图像,从而产生一个大旋转圆圈的错觉。 所以,让我们定义一个动画, m,并将其应用于图像元素:

.gallery > img {
  /* same as before */
  animation: m 8s infinite linear;
  transform-origin: 50% 120.7%;
}

@keyframes m {
  100% { transform: rotate(-360deg); }
}

主要技巧依赖于突出显示的行。 默认情况下,CSS transform-origin 财产等于 center (或 50% 50%) 这使得图像围绕其中心旋转,但我们不需要它来做那个。 我们需要图像围绕中心旋转 大圆圈 包含我们的图像因此新的价值 transform-origin.

由于 R 等于 0.707 * S,我们可以说 R 等于 70.7% 的图像大小。 这是一个图来说明我们是如何得到的 120.7% 值:

让我们运行动画看看会发生什么:

我知道我知道。 结果与我们想要的相去甚远,但实际上我们已经很接近了。 看起来那里可能只有一张图片,但不要忘记我们已经将所有图片堆叠在一起。 所有这些都在同时旋转,只有顶部图像是可见的。 我们需要的是延迟每个图像的动画以避免这种重叠。

.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 8s / 4 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 8s / 4 */
.gallery > img:nth-child(4) { animation-delay: -6s; } /* -3 * 8s / 4 */

情况已经好转了!

如果我们隐藏容器上的溢出,我们已经可以看到一个滑块,但我们将稍微更新动画,以便每个图像在移动之前在短时间内保持可见。

我们将更新我们的动画关键帧来做到这一点:

@keyframes m {
  0%, 3% { transform: rotate(0); }
  22%, 27% { transform: rotate(-90deg); }
  47%, 52% { transform: rotate(-180deg); }
  72%, 77% { transform: rotate(-270deg); }
  98%, 100% { transform: rotate(-360deg); }
}

对于每一个 90deg (360deg/4,其中 4 是图像的数量)我们将添加一个小的停顿。 每个图像将保持可见 5% 在我们滑到下一个之前的总持续时间(27%-22%, 52%-47%, ETC。)。 我要更新 animation-timing-function 使用 cubic-bezier() 使动画更漂亮的功能:

现在我们的滑块完美了! 好吧,几乎是完美的,因为我们仍然缺少最后的润色:围绕我们的图像旋转的彩色圆形边框。 我们可以使用伪元素 .gallery 包装器制作它:

.gallery {
  padding: calc(var(--s) / 20); /* the padding is needed here */
  position: relative;
}
.gallery::after {
  content: "";
  position: absolute;
  inset: 0;
  padding: inherit; /* Inherits the same padding */
  border-radius: 50%;
  background: repeating-conic-gradient(#789048 0 30deg, #DFBA69 0 60deg);
  mask: 
    linear-gradient(#fff 0 0) content-box, 
    linear-gradient(#fff 0 0);
  mask-composite: exclude;
}
.gallery::after,
.gallery >img {
  animation: m 8s infinite cubic-bezier(.5, -0.2, .5, 1.2);
}

我创建了一个圆圈 重复圆锥渐变 为背景而使用 掩蔽技巧 只显示填充区域。 然后我将我们为图像定义的相同动画应用于它。

我们完了! 我们有一个很酷的圆形滑块:

让我们添加更多图像

使用四张图像很好,但如果我们可以将其缩放到任意数量的图像会更好。 毕竟,这是图像滑块的目的。 我们应该可以考虑 N 图像。

为此,我们将通过引入 Sass 使代码更通用。 首先,我们为图像数量定义一个变量($n) 并且我们将更新我们硬编码图像数量的每个部分 (4).

让我们从延迟开始:

.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 8s / 4 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 8s / 4 */
.gallery > img:nth-child(4) { animation-delay: -6s; } /* -3 * 8s / 4 */

延迟的公式是 (1 - $i)*duration/$n,这给了我们以下 Sass 循环:

@for $i from 2 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
    animation-delay: calc(#{(1 - $i) / $n} * 8s);
  }
}

如果我们真的愿意,我们也可以将持续时间设为一个变量。 但是让我们继续看动画:

@keyframes m {
  0%, 3% { transform: rotate(0); }
  22%, 27% { transform: rotate(-90deg); }
  47%, 52% { transform: rotate(-180deg); }
  72%, 77% { transform: rotate(-270deg); }
  98%, 100% {transform: rotate(-360deg); }
}

让我们简化它以更好地了解模式:

@keyframes m {
  0% { transform: rotate(0); }
  25% { transform: rotate(-90deg); }
  50% { transform: rotate(-180deg); }
  75% { transform: rotate(-270deg); }
  100% { transform: rotate(-360deg); }
}

每个状态之间的步长等于 25% —这是 100%/4 - 我们添加一个 -90deg 角度——这是 -360deg/4. 这意味着我们可以像这样编写循环:

@keyframes m {
  0% { transform: rotate(0); }
  @for $i from 1 to $n {
    #{($i / $n) * 100}% { transform: rotate(#{($i / $n) * -360}deg); }  
  }
  100% { transform: rotate(-360deg); }
}

由于每张图片都需要 5% 的动画,我们改变这个:

#{($i / $n) * 100}%

…有了这个:

#{($i / $n) * 100 - 2}%, #{($i / $n) * 100 + 3}%

应当指出的是, 5% 是我为这个例子选择的任意值。 我们还可以使它成为一个变量来控制每个图像应该保持可见的时间。 为了简单起见,我将跳过它,但是对于家庭作业,您可以尝试这样做并在评论中分享您的实现!

@keyframes m {
  0%,3% { transform: rotate(0); }
  @for $i from 1 to $n {
    #{($i / $n) * 100 - 2}%, #{($i / $n) * 100 + 3}% { transform: rotate(#{($i / $n) * -360}deg); }  
  }
  98%,100% { transform: rotate(-360deg); }
}

最后一点是更新 transform-origin. 我们需要一些几何技巧。 无论图像数量多少,配置始终相同。 我们将图像(小圆圈)放在一个大圆圈内,我们需要找到半径值, R.

您可能不想要无聊的几何解释,所以这就是我们如何找到 R:

R = S / (2 * sin(180deg / N))

如果我们将其表示为百分比,则可以得出:

R = 100% / (2 * sin(180deg / N)) = 50% / sin(180deg / N)

……这意味着 transform-origin 值等于:

transform-origin: 50% (50% / math.sin(180deg / $n) + 50%);

我们完成了! 我们有一个适用于任意数量图像的滑块!

让我们在那里扔九张图片:

添加任意数量的图像并更新 $n 图像总数的变量。

结束了

通过使用 CSS 转换和标准几何的一些技巧,我们创建了一个不需要大量代码的漂亮的圆形滑块。 这个滑块的妙处在于我们不需要复制图像来保持无限动画,因为我们有一个圆圈。 完整旋转后,我们将回到第一张图片!

时间戳记:

更多来自 CSS技巧