使用 CSS 渐变和纵横比创建响应式图像效果
已发表: 2022-03-10aspect-ratio
属性与object-fit
相结合,为过去令人头疼的问题提供了一种补救措施! 让我们学习使用这些属性,以及创建响应式渐变图像效果以获得额外的天赋。为了准备我们未来的图像效果,我们将设置一个卡片组件,顶部有一个大图像,后面是标题和描述。 这种设置的常见问题是我们可能并不总是完美地控制图像是什么,更重要的是我们的布局,它的尺寸是什么。 虽然这可以通过提前裁剪来解决,但由于容器大小的响应,我们仍然会遇到问题。 结果是卡片内容的位置不均匀,当你出示一排卡片时,它真的很突出。
除了裁剪之外,另一个先前的解决方案可能是从内联img
交换到一个空白div
,该 div 仅存在于通过background-image
呈现图像。 过去我自己多次实施过这个解决方案。 这样做的一个优点是使用一种较旧的纵横比技巧,该技巧使用零高度元素并设置padding-bottom
值。 将填充值设置为百分比会产生相对于元素宽度的最终计算值。 您可能还使用这个想法来保持视频嵌入的 16:9 比例,在这种情况下,填充值可以通过以下公式找到: 9/16 = 0.5625 * 100% = 56.26%
。 但是我们将探索两个不涉及额外数学的现代 CSS 属性,给我们更多的灵活性,并且还允许通过使用真实的img
而不是空的div
来保持语义。
首先,让我们定义 HTML 语义,包括使用无序列表作为卡片的容器:
<ul class="card-wrapper"> <li class="card"> <img src="" alt=""> <h3>A Super Wonderful Headline</h3> <p>Lorem ipsum sit dolor amit</p> </li> <!-- additional cards --> </ul>
接下来,我们将为.card
组件创建一组最小的基线样式。 我们将为卡片本身设置一些基本的视觉样式,快速更新预期的h3
标题,然后开始设置卡片图像样式的基本样式。
.card { background-color: #fff; border-radius: 0.5rem; box-shadow: 0.05rem 0.1rem 0.3rem -0.03rem rgba(0, 0, 0, 0.45); padding-bottom: 1rem; } .card > :last-child { margin-bottom: 0; } .card h3 { margin-top: 1rem; font-size: 1.25rem; } img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; } img ~ * { margin-left: 1rem; margin-right: 1rem; }
最后一条规则使用通用兄弟组合器为img
之后的任何元素添加水平边距,因为我们希望图像本身与卡片的侧面齐平。
到目前为止,我们的进展导致我们出现以下卡片外观:
最后,我们将使用 CSS 网格创建快速响应布局的.card-wrapper
样式。 这也将删除默认列表样式。
.card-wrapper { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr)); grid-gap: 1.5rem; }
注意:如果您不熟悉这种网格技术,请查看我的教程中关于 12 列网格的现代解决方案的说明。
应用了这个并且所有卡片都包含具有有效源路径的图像,我们的.card-wrapper
样式为我们提供了以下布局:
如预览图像所示,这些基线样式不足以正确包含图像,因为它们具有不同的自然尺寸。 我们需要一种方法来统一和一致地约束这些图像。
启用具有object-fit
统一图像大小
如前所述,您之前可能已在此场景中进行了更新,以更改要通过background-image
添加的图像,并使用background-size: cover
来很好地调整图像大小。 或者您可能已经尝试提前强制裁剪(这仍然是一个有价值的目标,因为任何图像尺寸减小都会提高性能!)。
现在,我们有了object-fit
属性,它可以让img
标签充当图像的容器。 并且,它还带有一个cover
值,可以产生与背景图像解决方案类似的效果,但具有保留内联图像语义的好处。 让我们应用它,看看它是如何工作的。
我们确实需要将它与height
尺寸配对,以获得关于我们希望图像容器如何表现的额外指导(回想一下我们已经添加了width: 100%
)。 我们将使用max()
函数来选择10rem
或30vh
,具体取决于给定上下文中哪个更大,这可以防止图像高度在较小的视口或用户设置大缩放时缩小太多。
img { /* ...existing styles */ object-fit: cover; height: max(10rem, 30vh); }
额外的辅助功能提示:您应该始终在桌面上使用 200% 和 400% 缩放来测试您的布局。 虽然目前没有zoom
媒体查询,但max()
等函数可以帮助解决布局问题。 该技术有用的另一个上下文是元素之间的间距。
通过这次更新,我们确实改进了一些东西,视觉效果就好像我们使用了旧的背景图像技术:
响应一致的图像大小与aspect-ratio
当单独使用object-fit
时,一个缺点是我们仍然需要设置一些维度提示。
即将推出的属性(目前在 Chromium 浏览器中可用)称为aspect-ratio
,将增强我们一致地调整图像大小的能力。
使用这个属性,我们可以定义一个比率来调整图像的大小,而不是设置明确的尺寸。 我们将继续将它与object-fit
结合使用,以确保这些尺寸仅影响作为容器的图像,否则,图像可能会出现失真。
这是我们完整更新的图像规则:
img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; object-fit: cover; aspect-ratio: 4/3; }
对于卡片上下文,我们将从4 ⁄ 3的图像比例开始,但您可以选择任何比例。 例如, 1 ⁄ 1表示正方形,或16 ⁄ 9表示标准视频嵌入。
这是更新后的卡片,尽管在这个特定实例中可能很难注意到视觉差异,因为纵横比恰好与我们通过单独设置object-fit
height
获得的外观非常匹配。
设置 `aspect-ratio` 会导致比例随着元素的增长或缩小而保持不变,而当仅设置 `object-fit` 和 `height` 时,图像比例将随着容器尺寸的变化而不断变化。
“
使用 CSS 渐变和函数添加响应式效果
好的,现在我们知道了如何设置一致大小的图像,让我们通过添加渐变效果来享受它们的乐趣!
我们使用此效果的目标是让它看起来好像图像正在淡入卡片内容。 您可能很想将图像包装在自己的容器中以添加渐变,但是由于我们已经在图像大小方面所做的工作,我们可以弄清楚如何在主.card
上安全地执行此操作。
第一步是定义梯度。 我们将使用 CSS 自定义属性添加渐变颜色,以便轻松交换渐变效果,从蓝色到粉红色开始。 渐变中的最后一种颜色将始终为白色,以保持过渡到卡片内容背景并创建“羽化”边缘。
.card { --card-gradient: #5E9AD9, #E271AD; background-image: linear-gradient( var(--card-gradient), white max(9.5rem, 27vh) ); /* ...existing styles */ }
但是等等——这是一个 CSS max()
函数吗? 在渐变中? 是的,这是可能的,而且它是使这个渐变响应有效的魔力!
但是,如果我要添加屏幕截图,我们实际上不会看到渐变对图像有任何影响。 为此,我们需要引入mix-blend-mode
属性,在这种情况下,我们将使用overlay
值:
img { /* ...existing styles */ mix-blend-mode: overlay; }
mix-blend-mode
属性类似于应用 Photoshop 等照片处理软件中可用的图层混合样式。 并且overlay
值将具有允许图像中的中等色调与其后面的渐变混合的效果,从而导致以下结果:
现在,此时,我们仅依靠aspect-ratio
来调整图像大小。 如果我们调整容器大小并导致卡片布局重排,则不断变化的图像高度会导致渐变渐变为白色的位置不一致。
因此,我们还将添加一个max-height
属性,该属性也使用max()
函数并包含比渐变中的值稍大的值。 由此产生的行为是渐变将(几乎总是)正确地与图像底部对齐。
img { /* ...existing styles */ max-height: max(10rem, 30vh); }
需要注意的是,添加 `max-height` 会改变 `aspect-ratio` 行为。 它不会总是使用精确的比例,而是仅在给定“最大高度”的新额外约束时有足够的分配空间时使用。
“
但是, aspect-ratio
仍将继续确保图像大小一致地调整,这与仅object-fit
相比具有优势。 尝试在最终的 CodePen 演示中注释掉aspect-ratio
,以查看它在不同容器大小之间的差异。
由于我们最初的目标是实现一致的响应式图像尺寸,因此我们仍然达到了目标。 对于您自己的用例,您可能需要调整比率和高度值以达到您想要的效果。
替代方案: mix-blend-mode
并添加过滤器
使用overlay
作为mix-blend-mode
值是我们正在寻找的淡入白效果的最佳选择,但让我们尝试另一种选择以获得更戏剧性的效果。
我们将更新我们的解决方案,为mix-blend-mode
值添加 CSS 自定义属性,并更新渐变的颜色值:
.card { --card-gradient: tomato, orange; --card-blend-mode: multiply; } img { /* ...existing styles */ mix-blend-mode: var(--card-blend-mode); }
multiply
值对中间色调有暗化效果,但保持白色和黑色不变,从而产生以下外观:
虽然我们已经失去了淡入淡出并且现在在图像底部有一个硬边,但我们渐变的白色部分仍然很重要,以确保渐变在卡片内容之前结束。
我们可以添加的另一个修改是使用filter
,特别是使用grayscale()
函数来去除图像颜色,因此渐变是图像着色的唯一来源。
img { /* ...existing styles */ filter: grayscale(100); }
使用grayscale(100)
的值会导致完全去除图像的自然颜色并将其转换为黑白。 这是与之前使用我们的橙色渐变和multiply
效果的屏幕截图进行比较的更新:
使用aspect-ratio
增强
如前所述,目前仅最新版本的 Chromium 浏览器(Chrome 和 Edge)支持aspect-ratio
。 然而,所有的浏览器都支持object-fit
,再加上我们的height
限制,导致了一个不太理想但仍然可以接受的结果,在这里可以看到 Safari:
如果没有aspect-ratio
功能,这里的结果是最终图像高度被限制,但每个图像的自然尺寸仍然导致卡片图像高度之间的一些差异。 您可能希望改为添加max-height
或再次使用max()
函数来帮助使max-height
卡片尺寸下更具响应性。
扩展渐变效果
由于我们将渐变色标定义为 CSS 自定义属性,因此我们可以随时在不同的上下文中更改它们。 例如,如果卡片悬停或其中一个子元素处于焦点位置,我们可能会更改渐变以更强烈地显示其中一种颜色。
首先,我们将更新每张卡片h3
以包含一个链接,例如:
<h3><a href="">A Super Wonderful Headline</a></h3>
然后,我们可以使用我们最新的可用选择器之一 - :focus-within
- 在链接处于焦点时更改卡片渐变。 为了额外覆盖可能的交互,我们将把它与:hover
结合起来。 而且,我们将重用我们的max()
想法来分配一种颜色来接管卡片图像部分的覆盖范围。 这种特殊效果的缺点是渐变停止和颜色变化不能可靠地动画化——但由于 CSS Houdini,它们很快就会实现。
要更新颜色并添加新的色标,我们只需要在这个新规则中重新分配--card-gradient
的值:
.card:focus-within, .card:hover { --card-gradient: #24a9d5 max(8.5rem, 20vh); }
我们的max()
值小于用于white
以保持羽化边缘的原始值。 如果我们使用相同的值,它将遇到white
并创建一个清晰的直尺分离。
在创建这个演示时,我最初尝试了一个使用带有scale
的transform
来实现放大效果的效果。 但我发现,由于应用了mix-blend-mode
,浏览器不会始终如一地重新绘制图像,从而导致令人不快的闪烁。 请求浏览器执行纯 CSS 效果和动画总是需要权衡取舍,虽然我们可以做的很酷,但最好检查效果对性能的影响。
体验愉快!
现代 CSS 为我们提供了一些很棒的工具来更新我们的网页设计工具包,其中最新添加的aspect-ratio
。 因此,继续尝试object-fit
、 aspect-ratio
,并在渐变中添加诸如max()
之类的函数,以获得一些有趣的响应效果! 请务必仔细检查跨浏览器(现在!)以及不同的视口和容器大小。
这是 CodePen,包括我们今天回顾的功能和效果:
寻找更多? 确保您在 Smashing 上查看我们的 CSS 指南 →