图片滑块(也称为轮播)无处不在。有很多 创建常见滑块的 CSS 技巧,这些滑块会从左到右(或反方向)滑动图片。与 许多创建带复杂动画的精美滑块的 JavaScript 库 是一样的。我们不会在这篇文章中进行任何操作。
通过一系列文章,我们将探索一些奇特且不常见的纯 CSS 滑块。如果您厌倦了看到相同的经典滑块,那么您来对地方了!
在第一篇文章中,我们将从我称为“循环旋转图片滑块”的东西开始。
很酷,对吧?让我们分析一下代码!
HTML 标记
如果您关注我的 奇特图片装饰 或 CSS 网格和自定义形状 系列,那么您知道我的第一条规则是使用尽可能少的 HTML。我一直努力寻找 CSS 解决方案,而不是用大量<div>
和其它内容来使代码混乱。
同样的规则也适用于此 - 我们的代码只不过是在容器中的图片列表。
假设我们使用四张图片
<div class="gallery">
<img src="" alt="">
<img src="" alt="">
<img src="" alt="">
<img src="" alt="">
</div>
就是这样!现在让我们进入代码中有趣的部分。但首先,我们将深入了解它,以了解滑块工作原理的逻辑。
它是如何工作的?
这里有一段视频,我从中删除了 CSS 中的overflow: hidden
,这样我们可以更好地理解图片是如何移动的
就像我们的四张图片被放置在一个逆时针旋转的大圆圈上。
所有图片都具有相同的大小(在图中用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%
等)。我将更新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 变换和标准几何学的几个技巧,我们创建了一个不错的循环滑块,它不需要很多代码。这个滑块的酷炫之处在于,我们不需要为了保持无限动画而费心复制图片,因为我们有一个圆圈。在完整旋转之后,我们将回到第一张图片!
对于想要更多了解几何部分的任何人,这里有一张图
超级酷炫的技术,在这个单一演示中有很多宝贵的知识!感谢分享。
不错。简单。少即是多
我注意到每个图片都会旋转超过可见区域一点,然后又回到刚好合适的位置。这是什么原因呢?
是三次贝塞尔曲线造成的。在文章的某个地方,我将时间函数从线性修改为三次贝塞尔曲线。
有意思。我怀疑是三次贝塞尔曲线,但我不知道时间函数可以改变帧的顺序。我以为它只影响每一帧的持续时间。
太棒了
我遇到的最好的技巧之一。很棒的工作,非常简洁。
如果图片数量是动态的,会发生什么?如何使图片的 nth-ing 考虑 n 个图片?
我想知道正方形是否会类似甚至更容易?
有没有人尝试过用完美的正方形作为图片?