在这个系列中,我们一直在用 HTML 和 CSS 制作图像滑块。 我们的想法是,无论我们放入多少图像,我们都可以使用相同的标记但不同的 CSS 来获得截然不同的结果。我们从一个无限旋转的圆形滑块开始,有点像一个固定图像的指尖陀螺。 然后我们做了一个可以翻阅一堆照片的。
这一次,我们将进入三维空间。 起初看起来很难,但我们正在查看的许多代码正是我们在本系列前两篇文章中使用的代码,并进行了一些修改。 因此,如果您刚刚开始学习本系列,我建议您查看其他系列以了解我们在此使用的概念的上下文。
CSS 滑块系列
这就是我们的目标:
乍一看,我们似乎有一个带有四个图像的旋转立方体。 但实际上,我们总共要处理六张图片。 这是从不同角度看的滑块:
现在我们对图像的排列方式有了很好的了解,让我们剖析代码看看我们是如何实现的。
基本设置
与我们用于其他滑块的其余滑块相同的 HTML:
再一次,我们使用 CSS Grid 将图像放在一个堆栈中,一个在另一个之上:
.gallery {
display: grid;
}
.gallery > img {
grid-area: 1 / 1;
width: 160px;
aspect-ratio: 1;
object-fit: cover;
}
动画
此滑块的逻辑非常类似于 第一篇文章中的圆形滑块. 事实上,如果您再次查看上面的视频,您会发现图像以创建多边形的方式放置。 完整旋转后,它返回到第一个图像。
我们依赖于 CSS transform-origin
和 animation-delay
第一个滑块的属性。 相同的动画应用于围绕同一点旋转的所有图像元素。 然后,通过使用不同的延迟,我们正确地将所有图像放置在一个大圆圈周围。
我们的 3D 滑块的实现会有些不同。 使用 transform-origin
不会在这里工作,因为我们在 3D 中工作,所以我们将使用 transform
而不是正确放置所有图像,然后旋转容器。
我们再次使用 Sass,因此我们可以遍历图像数量并应用我们的转换:
@for $i from 1 to ($n + 1) {
.gallery > img:nth-child(#{$i}) {
transform:
rotate(#{360*($i - 1) / $n}deg) /* 1 */
translateY(50% / math.tan(180deg / $n)) /* 2 */
rotateX(90deg); /* 3 */
}
}
你可能想知道为什么我们要直接跳到 Sass。 我们在其他文章中使用 vanilla CSS 从固定数量的图像开始,然后使用 Sass 概括代码以说明任何数量(N
)的图像。 好吧,我想你现在明白了,我们可以省掉所有的发现工作,去真正的实施。
transform
属性采用三个值,我已在此处说明:
我们首先将所有图像旋转到彼此上方。 旋转角度取决于图像的数量。 为了 N
图像,我们有一个增量等于 360deg/N
。 然后我们 translate
以相同的方式将所有图像以使其中心点在两侧相交的方式。
![CSS 无限 3D 滑块 显示堆叠成一个圆圈的图像,红线穿过图像的中心点。](https://platoblockchain.com/wp-content/uploads/2022/12/s_FE940CE5633DC3CC268448BD2A737C637A3BBD3C79BC16A3787EDF75D79CD0EA_1669042365482_image.png)
有一些无聊的几何图形可以帮助解释这一切是如何工作的,但距离等于 50%/tan(180deg/N)
. 我们在制作圆形滑块时处理了类似的方程式( transform-origin: 50% 50%/sin(180deg/N)
).
最后,我们将图像绕 x 轴旋转 90deg
得到我们想要的安排。 这是一个视频,说明了最后一次轮换正在做什么:
现在我们所要做的就是旋转整个容器来创建我们的无限滑块。
.gallery {
transform-style: preserve-3d;
--_t: perspective(280px) rotateX(-90deg);
animation: r 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite;
}
@keyframes r {
0%, 3% {transform: var(--_t) rotate(0deg); }
@for $i from 1 to $n {
#{($i/$n)*100 - 2}%,
#{($i/$n)*100 + 3}% {
transform: var(--_t) rotate(#{($i / $n) * -360}deg);
}
}
98%, 100% { transform: var(--_t) rotate(-360deg); }
}
该代码可能难以理解,所以让我们实际上退后一步,重新审视我们为圆形滑块制作的动画。 这是我们在第一篇文章中写的:
.gallery {
animation: m 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite;
}
@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); }
}
关键帧几乎相同。 我们有相同的百分比值、相同的循环和相同的旋转。
为什么两者相同? 因为他们的逻辑是一样的。 在这两种情况下,图像都是围绕圆形排列的,我们需要旋转整个东西来显示每个图像。 这就是我如何能够从圆形滑块复制关键帧并将相同的代码用于我们的 3D 滑块。 唯一的区别是我们需要将容器旋转 -90deg
沿 x 轴查看图像,因为我们已经将它们旋转了 90deg
在同一个轴上。 然后我们添加一点 perspective
以获得 3D 效果。
而已! 我们的滑块完成了。 这是完整的演示。 您所要做的就是添加任意数量的图像并更新一个变量以使其运行。
垂直 3D 滑块
既然我们玩的是3D空间,为什么不把之前的滑块做成竖版呢? 最后一个沿 z 轴旋转,但如果需要,我们也可以沿 x 轴移动。
如果比较此滑块的两个版本的代码,您可能不会立即发现差异,因为它只有一个字符! 我换了 rotate()
rotateX()
在关键帧和图像中 transform
。 而已!
应当指出的是, rotate()
相当于 rotateZ()
,所以通过改变轴从 Z
至 X
我们将滑块从水平版本转换为垂直版本。
立方滑块
如果没有,我们就不能在 CSS 中谈论 3D 谈论立方体. 是的,这意味着我们将制作另一个版本的滑块。
这个版本的滑块背后的想法是用图像创建一个实际的立方体形状,并围绕不同的轴旋转整个物体。 因为它是一个立方体,所以我们要处理六个面。 我们将使用六张图片,一张代表立方体的每个面。 所以,没有 Sass,而是回到 vanilla CSS。
那个动画有点压倒性,对吧? 你甚至从哪里开始?
我们有六张脸,所以我们需要至少执行六次旋转,以便每张图像都转一圈。 好吧,实际上,我们需要五次旋转——最后一次将我们带回第一张图像的脸。 如果你去拿一个魔方——或者像骰子这样的其他立方体形状的物体——然后用手旋转它,你就会很清楚我们在做什么。
.gallery {
--s: 250px; /* the size */
transform-style: preserve-3d;
--_p: perspective(calc(2.5*var(--s)));
animation: r 9s infinite cubic-bezier(.5, -0.5, .5, 1.5);
}
@keyframes r {
0%, 3% { transform: var(--_p); }
14%, 19% { transform: var(--_p) rotateX(90deg); }
31%, 36% { transform: var(--_p) rotateX(90deg) rotateZ(90deg); }
47%, 52% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); }
64%, 69% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg); }
81%, 86% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg); }
97%, 100%{ transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); }
}
transform
属性从零旋转开始,并且在每个状态上,我们在特定轴上附加一个新的旋转,直到我们达到六次旋转。 然后我们回到第一张图片。
我们不要忘记图像的位置。 每个都应用到立方体的一个面上,使用 transform
:
.gallery img {
grid-area: 1 / 1;
width: var(--s);
aspect-ratio: 1;
object-fit: cover;
transform: var(--_t,) translateZ(calc(var(--s) / 2));
}
.gallery img:nth-child(2) { --_t: rotateX(-90deg); }
.gallery img:nth-child(3) { --_t: rotateY( 90deg) rotate(-90deg); }
.gallery img:nth-child(4) { --_t: rotateX(180deg) rotate( 90deg); }
.gallery img:nth-child(5) { --_t: rotateX( 90deg) rotate( 90deg); }
.gallery img:nth-child(6) { --_t: rotateY(-90deg); }
您可能认为我在那里使用的值背后有奇怪的复杂逻辑,对吗? 好吧,不。 我所做的只是打开 DevTools 并为每张图像使用不同的旋转值,直到我正确为止。 这听起来可能很愚蠢,但是,嘿,它有效——尤其是因为我们有固定数量的图像,而且我们不是在寻找支持的东西 N
图像。
事实上,忘记我正在使用的值并尝试自己进行放置作为练习。 从所有堆叠在一起的图像开始,打开 DevTools,开始吧! 您可能最终会得到不同的代码,这完全没问题。 可以有不同的方式来定位图像。
里面的逗号有什么用
var()
? 是打错了吗?
这不是打字错误,所以不要删除它! 如果删除它,您会注意到它会影响第一张图片的位置。 你可以在我的代码中看到我定义的 --_t
对于除第一个图像之外的所有图像,因为我只需要对其进行翻译。 该逗号使变量退回到空值。 没有逗号,我们将没有回退,整个值将无效。
从 规范:
注意:也就是说,
var(--a,)
是一个有效的函数,指定如果--a
自定义属性无效或丢失,var()
` 应该替换为空。
随机立方体滑块
一点点随机性可以很好地增强这种动画效果。 因此,与其按顺序旋转立方体,不如说我们可以掷骰子,让立方体随心所欲地滚动。
酷吧? 我不了解你,但我更喜欢这个版本! 它更有趣,并且转换令人满意。 你猜怎么着? 您可以使用这些值来创建您自己的随机立方体滑块!
逻辑实际上根本不是随机的——它只是看起来那样。 你定义一个 transform
在允许您显示一张脸的每个关键帧上……好吧,就是这样! 你可以选择任何你想要的订单。
@keyframes r {
0%, 3% { transform: var(--_p) rotate3d( 0, 0, 0, 0deg); }
14%,19% { transform: var(--_p) rotate3d(-1, 1, 0,180deg); }
31%,36% { transform: var(--_p) rotate3d( 0,-1, 0, 90deg); }
47%,52% { transform: var(--_p) rotate3d( 1, 0, 0, 90deg); }
64%,69% { transform: var(--_p) rotate3d( 1, 0, 0,-90deg); }
81%,86% { transform: var(--_p) rotate3d( 0, 1, 0, 90deg); }
97%,100% { transform: var(--_p) rotate3d( 0, 0, 0, 0deg); }
}
我使用 rotate3d()
这一次,但我仍然依靠 DevTools 来找到我觉得“正确”的价值观。 不要试图找到关键帧之间的关系,因为根本就没有关系。 我正在定义单独的转换,然后观察“随机”结果。 确保第一张图像分别是第一帧和最后一帧,并在其他每一帧上显示不同的图像。
您没有义务使用 rotate3d()
像我一样转变。 您还可以像我们在前面的示例中所做的那样链接不同的旋转。 玩一玩,看看你能想出什么! 我会等着你在评论部分与我分享你的版本!
结束了
我希望你喜欢这个小系列。 我们构建了一些有趣(有趣)的滑块,同时学习了很多关于各种 CSS 概念的知识——从网格放置和堆叠顺序,到动画延迟和转换。 我们甚至需要使用一些 Sass 来循环遍历一组元素。
我们为我们制作的每个滑块使用完全相同的 HTML 来完成这一切。 多么酷啊? CSS 非常强大,无需 JavaScript 的帮助就可以完成很多事情。