上一篇文章中,我们制作了一个非常酷炫的小型滑块(或者如果您更喜欢的话,可以称之为“旋转木马”),它以圆形方向旋转。这次,我们将制作一个可以翻阅宝丽来图片堆栈的滑块。
很酷吧?先别看代码,因为有很多东西需要解释。加入我吧?
此滑块的大部分HTML和CSS与我们上次制作的圆形滑块类似。事实上,我们使用的是完全相同的标记:
<code><div> <img alt="" src=""><img alt="" src=""><img alt="" src=""><img alt="" src=""> </div></code>
这是基本的CSS,它将我们的父级.gallery
容器设置为网格,所有图像都在彼此之上堆叠:
<code>.gallery { display: grid; width: 220px; /* 控制大小 */ } .gallery > img { grid-area: 1 / 1; width: 100%; aspect-ratio: 1; object-fit: cover; border: 10px solid #f2f2f2; box-shadow: 0 0 4px #0007; }</code>
到目前为止,没有什么复杂的。即使对于宝丽来风格的图像,我使用的也只是边框和阴影。您可能能够做得更好,因此请随意尝试这些装饰样式!我们将把大部分精力放在动画上,这是最棘手的部分。
此滑块的逻辑依赖于图像的堆叠顺序——是的,我们将使用z-index
。所有图像都以相同的z-index
值(2)开始,这将使堆栈中的最后一张图像位于顶部。
我们取最后一张图像,将其向右滑动,直到显示堆栈中的下一张图像。然后我们降低图像的z-index
值,然后将其滑回卡组。由于它的z-index
值低于其余图像,因此它成为堆栈中的最后一张图像。
这是一个简化的演示,它展示了这个技巧。将鼠标悬停在图像上以激活动画:
现在,想象一下相同的技巧应用于所有图像。如果我们使用:nth-child()
伪选择器来区分图像,那么这就是模式:
这就是我们的无限滑块!
如果您还记得上一篇文章,我只定义了一个动画并使用延迟来控制每张图像。我们在这里也将做同样的事情。让我们首先尝试可视化动画的时间线。我们将从三张图像开始,然后将其推广到任意数量 (N) 的图像。
我们的动画分为三个部分:“向右滑动”、“向左滑动”和“不动”。我们可以很容易地识别每张图像之间的延迟。如果我们认为第一张图像从 0s 开始,并且持续时间等于 6s,那么第二张图像将从 -2s 开始,第三张图像将从 -4s 开始。
<code><div> <img alt="" src=""><img alt="" src=""><img alt="" src=""><img alt="" src=""> </div></code>
我们还可以看到,“不动”部分占据了整个动画的三分之二 (2*100%/3),而“向右滑动”和“向左滑动”部分一起占据了三分之一——因此,每个部分都等于总动画的 100%/6。
我们可以这样编写动画关键帧:
<code>.gallery { display: grid; width: 220px; /* 控制大小 */ } .gallery > img { grid-area: 1 / 1; width: 100%; aspect-ratio: 1; object-fit: cover; border: 10px solid #f2f2f2; box-shadow: 0 0 4px #0007; }</code>
120% 是一个任意值。我需要一个大于 100% 的值。图像需要向右滑动,远离其余图像。为此,它需要移动至少其大小的 100%。这就是我选择 120% 的原因——以获得一些额外的空间。
现在我们需要考虑z-index
。别忘了,我们需要在图像滑出堆栈之后和滑回堆栈底部之前更新图像的z-index
值。
<code>.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 6s / 3 */ .gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 6s / 3 */</code>
我们不是在时间线上的 16.67% (100%/6) 点定义一个状态,而是在几乎相同的点 (16.66% 和 16.67%) 定义两个状态,其中z-index
值在我们将图像滑回卡组之前降低。
这就是我们将所有这些结合在一起时发生的情况:
嗯,滑动部分似乎工作正常,但是堆叠顺序全部混乱了!动画开始得很好,因为顶部的图像正在移动到后面……但是后续的图像并没有跟上。如果您注意到,序列中的第二张图像在下一张图像闪烁到其顶部之前返回到堆栈顶部。
我们需要仔细跟踪z-index
的变化。最初,所有图像的z-index
都是:2。这意味着堆叠顺序应该是……
<code>@keyframes slide { 0% { transform: translateX(0%); } 16.67% { transform: translateX(120%); } 33.34% { transform: translateX(0%); } 100% { transform: translateX(0%); } }</code>
我们滑动第三张图像并将其z-index
更新为:
<code>@keyframes slide { 0% { transform: translateX(0%); z-index: 2; } 16.66% { transform: translateX(120%); z-index: 2; } 16.67% { transform: translateX(120%); z-index: 1; } /* 我们在这里更新 z-order */ 33.34% { transform: translateX(0%); z-index: 1; } 100% { transform: translateX(0% ); z-index: 1; } }</code>
我们对第二张图像也这样做:
<code>我们的眼睛 ? --> 第三张 (2) | 第二张 (2) | 第一张 (2)</code>
……以及第一张图像:
<code>我们的眼睛 ? --> 第二张 (2) | 第一张 (2) | 第三张 (1)</code>
我们这样做,一切似乎都很好。但实际上并非如此!当第一张图像移动到后面时,第三张图像将开始另一个迭代,这意味着它返回到z-index
:2:
<code>我们的眼睛 ? --> 第一张 (2) | 第三张 (1) | 第二张 (1)</code>
因此,实际上我们根本没有所有图像的z-index
:2!当图像没有移动(即动画的“不动”部分)时,z-index
为 1。如果我们滑动第三张图像并将其z-index
值从 2 更新为 1,它将保留在顶部!当所有图像具有相同的z-index
时,源顺序中的最后一个图像——在这种情况下是我们的第三张图像——位于堆栈顶部。滑动第三张图像会导致以下结果:
<code>我们的眼睛 ? --> 第三张 (1) | 第二张 (1) | 第一张 (1)</code>
第三张图像仍然位于顶部,之后,当它的动画以z-index
:2 重新启动时,我们将第二张图像移动到顶部:
<code><div> <img alt="" src=""><img alt="" src=""><img alt="" src=""><img alt="" src=""> </div></code>
一旦我们滑动它,我们得到:
<code>.gallery { display: grid; width: 220px; /* 控制大小 */ } .gallery > img { grid-area: 1 / 1; width: 100%; aspect-ratio: 1; object-fit: cover; border: 10px solid #f2f2f2; box-shadow: 0 0 4px #0007; }</code>
然后第一张图像将跳到顶部:
<code>.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 6s / 3 */ .gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 6s / 3 */</code>
我知道,这很令人困惑。但是我们的逻辑并不完全错误。我们只需要稍微纠正一下动画即可使一切按我们想要的方式工作。诀窍在于正确重置z-index
。
让我们考虑第三张图像位于顶部的这种情况:
<code>@keyframes slide { 0% { transform: translateX(0%); } 16.67% { transform: translateX(120%); } 33.34% { transform: translateX(0%); } 100% { transform: translateX(0%); } }</code>
我们看到滑动第三张图像并更改其z-index
会使其保持在顶部。我们需要做的是更新第二张图像的z-index
。因此,在我们滑动第三张图像远离卡组之前,我们将第二张图像的z-index
更新为 2。
换句话说,我们在动画结束之前重置第二张图像的z-index
。
绿色加号表示将z-index
增加到 2,红色减号与z-index
:1 相关。第二张图像以z-index
:2 开始,然后当它从卡组中滑出时,我们将它更新为 1。但在第一张图像从卡组中滑出之前,我们将第二张图像的z-index
更改回 2。这将确保两张图像具有相同的z-index
,但是,第三张图像将仍然位于顶部,因为它在DOM中出现较晚。但在第三张图像滑动并更新其z-index
之后,它会移动到底部。
这是动画的三分之二,因此让我们相应地更新关键帧:
<code>@keyframes slide { 0% { transform: translateX(0%); z-index: 2; } 16.66% { transform: translateX(120%); z-index: 2; } 16.67% { transform: translateX(120%); z-index: 1; } /* 我们在这里更新 z-order */ 33.34% { transform: translateX(0%); z-index: 1; } 100% { transform: translateX(0% ); z-index: 1; } }</code>
好了一点,但仍然不够好。还有一个问题……
别担心,我们不会再次更改关键帧,因为这个问题只在涉及最后一张图像时才会发生。我们可以为最后一张图像制作一个“特殊”的关键帧动画来修复问题。
当第一张图像位于顶部时,我们有以下情况:
<code>我们的眼睛 ? --> 第三张 (2) | 第二张 (2) | 第一张 (2)</code>
考虑到我们之前所做的调整,在第一张图像滑动之前,第三张图像将跳到顶部。这只会发生在这种情况下,因为在第一张图像之后移动的下一张图像是 DOM 中顺序较高的最后一张图像。其余图像很好,因为我们有 N,然后是 N - 1,然后我们从 3 到 2,从 2 到 1……但然后我们从 1 到 N。
为了避免这种情况,我们将对最后一张图像使用以下关键帧:
<code>我们的眼睛 ? --> 第二张 (2) | 第一张 (2) | 第三张 (1)</code>
我们在动画进行到 5/6 时(而不是三分之二)重置z-index
值,此时第一张图像已移出堆栈。所以我们看不到任何跳跃!
成功!我们的无限滑块现在完美无缺了!这是我们最终代码的全部内容:
<code>我们的眼睛 ? --> 第一张 (2) | 第三张 (1) | 第二张 (1)</code>
现在我们的动画适用于三张图像,让我们使其适用于任意数量 (N) 的图像。但首先,我们可以通过将动画拆分来稍微优化我们的工作,以避免冗余:
<code><div> <img alt="" src=""><img alt="" src=""><img alt="" src=""><img alt="" src=""> </div></code>
现在代码更易于阅读和维护了!我们为滑动部分制作一个动画,为z-index
更新制作另一个动画。请注意,我们在z-index
动画上使用steps(1)
。这是因为我希望突然更改z-index
值,与滑动动画不同,在滑动动画中我们希望平滑移动。
现在代码更易于阅读和维护,我们可以更好地了解如何支持任意数量的图像。我们需要做的是更新动画延迟和关键帧的百分比。延迟很容易,因为我们可以使用我们在上一篇文章中制作的完全相同的循环来支持圆形滑块中的多个图像:
<code>.gallery { display: grid; width: 220px; /* 控制大小 */ } .gallery > img { grid-area: 1 / 1; width: 100%; aspect-ratio: 1; object-fit: cover; border: 10px solid #f2f2f2; box-shadow: 0 0 4px #0007; }</code>
这意味着我们正在从普通的CSS转向Sass。接下来,我们需要想象一下时间线如何随着 N 张图像而变化。别忘了动画分为三个阶段:
在“向右滑动”和“向左滑动”之后,图像应该保持不动,直到其余图像完成序列。因此,“不动”部分需要花费与 (N - 1) 相同的时间作为“向右滑动”和“向左滑动”。在一个迭代中,N 张图像将滑动。因此,“向右滑动”和“向左滑动”都占据总动画时间线的 100%/N。“向右滑动”和“向左滑动”都占据总动画时间线的 100%/N。图像在 (100%/N)/2 时从堆栈中滑出,并在 100%/N 时滑回。
我们可以更改这个:
<code>.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 6s / 3 */ .gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 6s / 3 */</code>
……到这个:
<code>@keyframes slide { 0% { transform: translateX(0%); } 16.67% { transform: translateX(120%); } 33.34% { transform: translateX(0%); } 100% { transform: translateX(0%); } }</code>
如果我们将 N 替换为 3,当堆栈中有 3 张图像时,我们将得到 16.67% 和 33.33%。堆叠顺序的逻辑相同,我们将有:
<code>@keyframes slide { 0% { transform: translateX(0%); z-index: 2; } 16.66% { transform: translateX(120%); z-index: 2; } 16.67% { transform: translateX(120%); z-index: 1; } /* 我们在这里更新 z-order */ 33.34% { transform: translateX(0%); z-index: 1; } 100% { transform: translateX(0% ); z-index: 1; } }</code>
我们仍然需要更新 66.33% 的点。这应该是图像在动画结束之前重置其z-index
的地方。与此同时,下一张图像开始滑动。由于滑动部分需要 100%/N,因此重置应该发生在 100% - 100%/N:
<code>我们的眼睛 ? --> 第三张 (2) | 第二张 (2) | 第一张 (2)</code>
但是为了使我们的z-order-last
动画工作,它应该在序列中稍后发生。还记得我们对最后一张图像所做的修复吗?重置z-index
值需要在第一张图像移出堆栈时发生,而不是在它开始滑动时发生。我们可以在关键帧中使用相同的推理:
<code>我们的眼睛 ? --> 第二张 (2) | 第一张 (2) | 第三张 (1)</code>
我们完成了!以下是使用五张图像时得到的结果:
我们可以添加一点旋转来使事情变得更漂亮:
我所做的只是将rotate(var(--r))
附加到transform
属性。在循环中,--r
使用随机角度定义:
<code>我们的眼睛 ? --> 第一张 (2) | 第三张 (1) | 第二张 (1)</code>
旋转会产生小的故障,因为我们有时可以看到一些图像跳到堆栈的后面,但这并不重要。
所有这些z-index
工作都是一个巨大的平衡行为,对吧?如果您在此练习之前不确定堆叠顺序的工作方式,那么您现在可能对它有了更好的了解!如果您发现某些解释难以理解,我强烈建议您再次阅读本文并使用铅笔和纸张绘制内容。尝试使用不同数量的图像来说明动画的每个步骤,以更好地理解这个技巧。
上次,我们使用了一些几何技巧来创建一个圆形滑块,该滑块在完成一个完整序列后旋转回第一张图像。这次,我们使用z-index
完成了类似的技巧。在这两种情况下,我们都没有复制任何图像来模拟连续动画,也没有使用 JavaScript 来帮助进行计算。
下次,我们将制作 3D 滑块。敬请期待!
以上是CSS无限滑块翻过宝丽来图像的详细内容。更多信息请关注PHP中文网其他相关文章!