以下是 Dylan Winn-Brown 的客座文章,他向我们展示了一种实现此设计效果的高性能方法。
最近在为客户的网站工作时,我被要求复制类似于 此效果。

这种效果通常用于作品集类型的场景,设计意图是展示视觉和信息细节。
有很多不同的方法
由于我以前从未创建过这样的效果,所以我开始研究不同的实现方式,并发现了许多不同的方法。
一种选择是使用 jQuery 插件。 这个插件的效果不是我想要的,而且肯定不轻量级。
另一种选择是在容器内放置一个 <img>
并用 CSS 操作它。 这可能有一些潜在的好处,比如能够使用 srcset
设置源 以使使用的图片适合性能和设备。
在我的情况下,我想完全用 CSS 来管理这个效果,所以我选择了这种方法。
基本功能
为了获得最佳性能,我决定使用 CSS 的 transform
属性来处理图像的放大。(CSS 动画受益于硬件加速,因此看起来比其他动画方法更流畅。)
我没有使用 <img>
,而是在父元素内部使用了额外的 <div>
作为图像。 结构如下:
<div class="parent">
<div class="child"></div>
</div>
首先我们指定父元素的尺寸。 然后子元素可以使用 width: 100%
和 height: 100%;
填充父元素,并设置背景图像,确保它缩放以覆盖区域。
.parent {
width: 400px;
height: 300px;
}
.child {
width: 100%;
height: 100%;
background-color: black; /* fallback color */
background-image: url("images/city.jpg");
background-position: center;
background-size: cover;
}
然后我们向父元素添加悬停效果,这将影响我们的子元素。 焦点样式也对辅助功能很有用。
.parent:hover .child,
.parent:focus .child {
transform: scale(1.2);
}
您可能希望使用 工具 添加前缀以获得最佳的浏览器支持。
要完成基本效果,我们可以向子元素的正常状态添加一些过渡。
transition: all .5s;
如果要添加颜色叠加层,可以使用伪元素,例如 ::before
。
.child::before {
content: "";
display: none;
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background-color: rgba(52, 73, 94, 0.75);
}
.parent:hover .child:before,
.parent:focus .child:before {
display: block;
}
现在,当我们悬停在父元素上时,子元素应该显示颜色叠加层!
最后,我们将介绍如何在叠加层上添加一些文本。 我们可以像这样向当前子元素添加一个元素
<div class="parent">
<div class="child">
<span>Hello</span>
</div>
</div>
我们可以为我们的 <span>
设置一些样式
span {
color: white; /* Good thing we set a fallback color! */
font-family: sans-serif;
padding: 25%;
position: absolute;
}
并且我们可以在悬停在 .parent
上时才使其可见
.parent:hover span,
.parent:focus span {
display: block;
}
在线演示
查看 CodePen 上 Dylan (@dwinnbrown) 的作品 鼠标悬停时图片缩放 - 作品集网站。
移动设备支持
如果容器是链接,并且悬停状态没有显示任何重要信息,您可能只需将其保留。
如果悬停状态很重要,为了使其在触摸屏上正常工作,我们可以在 .parent
容器上使用空的 onclick=""
处理程序。 不幸的是,我没有找到其他方法,但如果您有,请在评论中告诉我!
我认为如果元素有
cursor: pointer
,iOS 会分派悬停状态;它是唯一的一个吗?移动设备支持 - 在我的 Kindle(第五代/2015)上的 Silk 浏览器中有效
我知道您试图展示这种效果,但我建议将其做得更微妙一些。 许多所谓的程序员会访问您的网站并直接复制粘贴。 然后我们就会在各个地方看到这种令人眼花缭乱的效果。 感谢您的考虑。
——
例如 http://codepen.io/anon/pen/GqPqpG
谢谢
您没有讨论开头段落中提到的 Fitbit 网站使用的实现该效果的方法(以及我最初考虑使用的方法),即使用
background-size
属性。使用背景图片并在悬停时对尺寸进行动画处理感觉是一种可靠的方法,因为它将所有代码都包含在样式表中,正如您提到的要求一样,并且不会为了实现样式问题而向 HTML 添加任何额外的元素,就像您使用的方法那样。
问题在于
background-size
的动画效果不如 transform 流畅。 我也喜欢代码的简洁和易用性,但仅使用 transform 的性能优势值得一提。使用背景图片制作图片库总让我觉得不太对劲。
我知道重点是尝试只使用一些
<div>
标签和 CSS 来应用此效果,但我还是为任何想要使用<img>
标签的人创建了一个版本:http://codepen.io/anon/pen/EyGmQx而且它也是一个讨厌的资源占用大户。 只要我将鼠标悬停在图片上,CPU 峰值就会达到 11……所以……不,不是我的(这是在一台简单的 i5-2520 / T520 上)。
恕我直言,背景解决方案的优势在于,调整大小 a) 更流畅(并且更少占用资源)和 b) 与媒体查询配合使用很有趣 = 不需要涉及当前“响应式图片”的糟糕状态。
再见,w0lf。
我还想指出
background-image
的另一个问题 - 如果您从服务器获取图像源,则无法像使用常规<img>
标签那样轻松地使用它们。@fwolf,我不确定为什么在将鼠标悬停在图像上时会看到 CPU 尖峰,因为 CSS 转换将由 GPU 处理。
如果使用背景图片确实存在某种性能提升,那么应该使用 JavaScript 通过渐进增强来替换图像为背景图像版本,但我并不相信存在性能问题。
我对背景图片的主要不满在于关注点分离、语义和可访问性。 随着互联网的发展,最佳实践似乎越来越屈服于视觉效果。
您的背景图片演示存在问题。 视口(演示页面可见区域)在将鼠标悬停在图像上和移开时会自动上下滚动。(在我的 [MacBook] 显示器上无法完整显示图像。)在 CSS-Tricks 演示中不会发生这种情况。
为什么不将这些想法结合起来,将背景图片放在 ::before 中,对它进行转换,并以屏幕阅读器仍然可以访问的方式隐藏图像元素。 此外,figure 和 figcaption 元素肯定比 div.parent、div.child 和 span 更可取。
小补充:我同意的另一个评论是动画太强烈了。我发现大约 0.9 秒的转换时间和 5% 到 10% 的缩放比例更令人愉悦。这种效果应该很微妙。但这当然很主观。
请问为什么容器中还有另一个
<div>(class 'child')
,而不是使用伪元素?没有必要使代码复杂化。此外,在这种情况下,语义化可能更好。他已经在子元素本身使用了伪元素。
如果将子元素定位为绝对定位,top 0,left 0 等等,您可以更容易地控制 Z 轴顺序。如果您想添加带有 .sibling 的图层以进行图像效果(例如使用内部阴影的晕影),这将非常有用。您还应该使用 visibility 和 opacity,而不是 display none,因为您无法对其进行动画/过渡。在 Codepen 上关注我 @CodeBoomer 和 YouTube +Boomer,我将在有时间的时候在几天内创建一个示例。
为了获得更好的性能,您可以使用
will-change:transform;
将子元素移动到其自己的 GPU 层。我做了一个快速演示,在我的机器上,实际动画期间的帧时间减少了一半!+1
太棒了!
我更喜欢从 <1 缩放到 1,而不是从 1 缩放到更高,这样就不会模糊图像。
使用 CSS,我想它会使用一个 DIV 来保持图像固定并始终覆盖显示的 100%。当然,CSS 和 HTML 是由服务器生成并发送的,因此您应该使用 javascript 调整 div 的大小。
当然,CSS 和 HTML 是由服务器生成并发送的,因此您应该使用 javascript 调整 div 的大小。
可能使用实际的图像元素更好(更具语义),您可以获得与背景图像相同的控制(使用 object-fit 而不是 background-size,以及用于不同尺寸的 srcset),您唯一需要做的是创建图像的正确纵横比(您已经介绍过了) https://css-tricks.cn/snippets/sass/maintain-aspect-ratio-mixin/
将该默认的线性过渡替换为三次贝塞尔曲线,瞧,到处都是性感的缩放!
移动设备的一种可能的(尽管几乎没有语义)解决方案是使用隐藏的单选按钮及其关联的标签:您隐藏
input
,将label
包裹在您想要显示/隐藏的内容周围,然后使用checked
属性(特性?)在您想要的状 态之间切换,无需任何 JavaScript。此页面使用此技术的示例: http://coxlawpdx.com/results/缺点:毫无语义意义;无法真正取消选择所有项目。
我更喜欢在覆盖层和文本出现时对其进行动画处理,这样就不会像在
display: none
和display: block
之间切换那样突然。这是一个示例,我使用visibility
和opacity
属性来实现更平滑的过渡http://codepen.io/praliedutzel/pen/GqPXPo
我喜欢那个。很好的补充。
+1
首先,感谢 Dylan Winn-Brown 与我们分享这种非常棒的效果。
这是我的贡献
我尝试只使用两个元素,没有任何 :before 或 :after 伪元素。文本最初设置为“color: transparent;”,然后通过过渡动画设置为“color: #f2f2f2;”。
通过在悬停状态下使用“background-blend-mode”和“background-color”,我创建了更暗的背景。
这是 codepen 的链接: http://codepen.io/anon/pen/oLJaOB
好的,我创建了这个笔: http://codepen.io/CodeBoomer/pen/ZOVPZb
我有
– 使用内联样式注入背景图像以复制 JS,因为您不会在 CSS 中声明它
– 为 (父元素)创建了 3 个子元素,因为:
a) 图像需要它自己的子节点,因为您在悬停时缩放此元素。通过应用 will-change,这将转换隔离到图像本身
b) 任何图像过滤器都可以在伪元素中使用(尽管在转换时性能会很差)
c) 悬停颜色覆盖不需要转换(我应用了径向渐变和阴影框以创建晕影)
d) 您可以用其自身节点中的名称/描述来弥补可访问性。此外,您可以使用不同的时间对其进行转换以获得半透视效果
e) 使控制堆叠顺序更容易
使用 visibility 和 opacity 而不是 display: none/block,因为 display 不可动画。
创建了一个简单的 flexbox 布局来容纳这些元素。
即使我 fork 了笔,我也对其进行了剥离并重建了整个内容,并使用了 SASS。
使用 flex 属性扩展了 CSS 代码,使其更容易理解。
为绝对定位创建了自己的 mixin,因为谁想写长版本。
感谢任何反馈/批评。请记住在 Codepen 上关注我,我很快将在我的 YouTube 上的 Let's Codepen 系列中发布此视频。订阅我的频道https://www.youtube.com/channel/UCydykDsTWRIVnxKHW3SHPQA
感谢大家的反馈——希望这对你们中的一些人有所帮助,如果你们将来需要类似的效果。
虽然它使用 SASS,但此代码 ^ 在我见过的替代方法中似乎具有最佳性能 :)
我唯一更改的是子伪元素,而不是执行“display:none”和“display:block”,为什么不只是将 alpha 更改为 0 和 .75 并删除 display 属性?
.child::before {
content: “”;
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background-color: rgba(52, 73, 94, 0);
}
.parent:hover .child:before,
.parent:focus .child:before {
background-color: rgba(52, 73, 94, 0.75);
}
关于“移动设备”支持的想法
我使用了一个小的特性检测 JS。如果有触摸支持,我将 body 类“no-touch”替换为“has-touch”。
这使我能够永久显示标题。因此用户获得了更好的体验。在您的演示案例中,用户无需猜测图像的位置。
此外,我将整合 @praliedutzel 和 @Filip Bech-Larsen 的建议,这将使演示接近完美。