这项技术探讨了使用:
object-fit用于响应式图像缩放- 一个用于从绝对位置升级的CSS网格 "黑客"。
- 用于动画效果的CSS变换
如果你曾经使用jQuery和position: absolute ,摆弄过图像标题动画的解决方案,或者试图处理宽度或高度的动画--而且都是在响应式图像还没有被关注之前发生的--那么这次升级就非常适合你!
关于响应式图像缩放的object-fit 的有用介绍,请查看本系列的早期文章。
画廊HTML
这是我们最初的HTML,这是一个ul ,其中每个li "卡片 "都包含图像和标题:
<ul class="gallery" role="list">
<li>
<figure>
<img alt="" src="https://picsum.photos/550/300" />
<figcaption>Candy canes ice cream</figcaption>
</figure>
</li>
<li>
<figure>
<img alt="" src="https://picsum.photos/400" />
<figcaption>Ice cream biscuit</figcaption>
</figure>
</li>
<li>
<figure>
<img alt="" src="https://picsum.photos/600/450" />
<figcaption>Cream biscuit marzipan</figcaption>
</figure>
</li>
</ul>
我使用了不同的图片尺寸,既是为了展示object-fit 在适应其容器方面的工作,也是为了减少来自picsum服务的重复图片的机会。
基本图库样式
由于我们使用了一个列表,我们需要删除默认的列表样式,我们还将把列表设置为一个网格容器:
.gallery {
list-style: none;
padding: 0;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(20ch, 1fr));
gap: 1rem;
}
.gallery img {
display: block;
width: 100%;
}
这些初始样式实现了将我们的列表项放在一行中,但并不限制图像:

画廊卡片和图像样式
如果你像我一样,在过去的几年里试图这样做,你可能会把你的滚珠鼠标扔到房间的另一边,试图弄清楚为什么position: absolute 与你的jQuery动画玩得不愉快。
CSS Grid和CSS transforms是来拯救你的。🎉
我们定义卡片将使用网格,并设置一个高度。我们仍将使用老式的overflow: hidden ,以帮助包含图像并确保标题最初被隐藏:
.gallery figure {
--gallery-height: 15rem;
/* reset figure default margin */
margin: 0;
height: var(--gallery-height);
background-color: hsl(200, 85%, 2%);
}
接下来,我们将object-fit 和width: 100% 、height: 40vh 一起应用到图片上,使其与卡片的大小相适应。object-fit: cover 的神奇之处在于它不会发生变形:
.gallery img {
display: block;
width: 100%;
object-fit: cover;
height: var(--gallery-height);
}

确定标题的位置
现在,根据Grid,标题已经自然地流向了图片的下面,因为它被认为应该在自己的 "单元格 "中,而且默认的网格项目是沿着Y轴流的。

为了解决这个问题,我们为.gallery-card 创建一个名为grid-template-areas 的单元格,并将.gallery-card__img 和.gallery-card__caption 都分配到这里。然后,我们将使用网格定位,在卡片上设置place-items: end ,将标题移到 "单元格 "的右下方。
.gallery figure {
/* ...existing styles */
display: grid;
grid-template-areas: "card";
place-items: end;
border-radius: 0.5rem;
overflow: hidden;
}
.gallery figure > * {
grid-area: card;
}
.gallery figcaption {
transform: translateY(100%);
}
导致以下结果:

为了放置标题,我们使用CSS变换来设置卡片外的初始位置与:
.gallery figcaption {
transform: translateY(100%);
transition: transform 800ms ease-in;
/* Visual styles for the caption */
padding: 0.25em 0.5em;
border-radius: 4px 0 0 0;
background-color: hsl(0 0% 100% / 87%);
}
.gallery figure:hover figcaption {
transform: translateY(0);
}
由于我们使用了一个段落元素,我们也删除了margin ,以防止它对标题的高度产生不利影响。translate的值为100% ,将使元素100% ,相对于它所处的轴线移动。因此,translateY(100%) 有效地将标题 "向下 "移出了初始视图。
为标题制作动画
我们的动画将在悬停时触发,我们希望它能顺利地动画化,然后再回到悬停。有两个属性可以帮助实现这个目标:transition 和will-change :
.gallery figcaption {
transform: translateY(100%);
transition: transform 800ms ease-in;
/* Visual styles for the caption */
padding: 0.25em 0.5em;
border-radius: 4px 0 0 0;
background-color: hsl(0 0% 100% / 87%);
}
.gallery figure:hover figcaption {
transform: translateY(0);
}
在这里,我们定义我们期望在transform 属性上有一个过渡,过渡持续时间应该是800ms ,并使用ease-in 的计时功能。然后,我们使用该 will-change属性作为对浏览器的额外提示,如果浏览器能够对该属性进行优化,transform 属性将被改变,以实现更流畅的过渡。
:hover 样式实际上将被放置在.gallery-card 上,因为它是包含元素,所以我们将添加一个transform 定义,通过将标题返回到 y 轴上的位置0 来将其移回到固有的起点。
.gallery-card:hover { .gallery-card__caption { transform: translateY(0); }}
然后嗒嗒嗒!我们有了一个基本的动画标题。

这个GIF比真正的动画更粗糙。
肯-伯恩斯图像效果#
你可能不知道这个名字,但你已经看到了这个效果:一个缓慢、平滑的平移和缩放组合的静止图像,由于被纪录片导演Ken Burns推广而得名。
利用我们已经介绍过的transition 和tranform 属性的原理,我们可以再次在.gallery-card__img 上组合它们,以便在悬停时也添加这种效果。
我们给transform 添加一个额外的值,将初始图像放大一个系数1.2 。这对于确保图像可以平移是必要的。如果我们把它留在简单的width: 100% ,一旦我们平移,它将不再覆盖卡片,卡片的背景将显示在空出的空间里。
.gallery img {
transform: scale(1) translate(0, 0);
transition: transform 1200ms ease-in;
}
此外,我们一开始就设置了一个5%的x-offset,这意味着图像被拉到右边5%。这适用于图像的缩放尺寸。
接下来我们将:hover 过渡到.gallery-card 规则,既为放大效果增加了一点比例,此外还在X轴上向左拉到-1% ,在Y轴上也增加了一点-3% 。
.gallery figure:hover img {
transform: scale(1.3) translate(-8%, -3%);
}
你可以根据自己的喜好调整翻译值。
还有一件事,就是我们设定的过渡持续时间有400ms的差异。我们可以添加这个值作为标题的延迟。请注意,这个延迟是在悬停时的过渡之前,以及在悬停后的过渡结束时。我个人喜欢这种效果,因为它意味着两个方向的动画都是一起结束的。
.gallery figcaption {
/* update to add the 400ms delay */
transition: transform 800ms 400ms ease-in;
}
不要忘记:focus#
悬停对于鼠标使用者来说是很好的,但对于那些由于各种原因主要使用键盘来导航的人来说,又该怎么办呢?
li 元素本来就不是一个可关注的元素,所以仅仅添加:focus 样式不会改变行为。
tabindex="0" 我们首先需要在每个li ,这将使它们成为可关注的元素。
.gallery figcaption {
transform: translateY(100%);
/* provide stacking context */
z-index: 1;
}
@media (any-hover: hover) and (any-pointer: fine) {
.gallery figcaption {
transform: translateY(100%);
}
}
你可以通过标签进行测试,你会注意到标准的光环边界。
我们将删除它,然后简单地将:focus 状态添加到我们现有的悬停规则中,这样它就适用于两者。
@media (prefers-reduced-motion: reduce) {
.gallery * {
transition-duration: 0ms !important;
}
.gallery img {
transform: none !important;
}
.gallery figcaption {
transition-delay: 0ms;
}
}
可选的Vignette
这种风格的另一个标志是光晕--图像边框上的柔和黑色渐变。我们可以通过一个嵌入的box-shadow 。诀窍在于,嵌入box-shadow 不会直接作用于图像元素,因此我们将其应用于:after 的伪元素.gallery-card 。
.gallery figure::after {
content: "";
grid-area: card;
width: 100%;
height: 100%;
box-shadow: inset 0 0 2rem 1rem hsl(0 0% 0% / 65%);
position: relative;
}
镶边的定位方法是将其应用于名为grid-area 的单一元素,并确保它有一个height 和width ,以占据整个卡片,此外还有一个z-index ,将其弹出在图像之上。
其副作用是它覆盖了标题,但我们将再添加一个z-index ,将其提升到2 ,以获得字面的最高优先权。
总的来说,只需添加一点额外的body 呈现风格。