visibility:hidden与display:none与opacity:0有何不同?

109

我目前正在开始一个动画项目。在这个项目中,我将拥有超过40000个div并对它们进行迭代动画。如果任何一个div处于被动状态(即至少2秒内没有进行动画),我将不会显示它们以提高动画性能。

问题是:哪种CSS属性最适合这种情况?

.passive1{
   display:none
}

.passive2{
    visibility:hidden;
}

.passive3{
    opacity:0;
}

我该如何衡量渲染性能,例如帧率和GPU使用情况?


11
如果你正在创建四万个div动画,那么你的性能会受到影响。也许你应该考虑使用canvas/flash。 - Undefined
我认为在canvas中编写这样的动画很困难,因为canvas中没有变换属性。在canvas中也没有translate、rotate函数。或者有吗? - Cihad Turhan
在SVG中,存在着 - Fabian Schmengler
4
是否将元素的opacity属性设置为0和将其visibility属性设置为hidden具有完全相同的效果?这个问题已经在stackoverflow.com上得到回答。 - givanse
更好的选择是生成SVG,然后将其转换为Canvas并从DOM中删除旧的SVG。这将为您提供性能提升。我已经测试过了,有很好的性能提升。 - Shashwat Tripathi
我会更倾向于在像你这样的大型动画项目中使用WebGL。Three.js还具有SVG渲染作为备选方案。 - atevm
8个回答

353

虽然这三个属性都会使元素的框看起来似乎是不可见的,但它们之间存在着关键性的差异:

属性 是否绘制 参与布局 形成堆叠上下文 响应指针事件 响应键盘事件
opacity: 0; 新的
visibility: hidden; 不确定
display: none; 不确定
  • “是否绘制”列表示浏览器是否会绘制元素的背景(例如background-image)、#text内容等等。
    • 当然,一个元素如果没有参与页面的布局,就不可能被绘制。
    • 对于所有三个属性和值来说,“是否绘制”列都是“否”,因为浏览器不需要绘制不可见的元素。
  • “参与布局”列表示浏览器是否会计算元素及其后代元素(未被排除在布局之外)的布局和尺寸。
    • 只有display: none;才是“否”,因为使用opacity: 0;visibility: hidden;时,浏览器仍然需要确定元素的大小以便正确布局其他元素(例如,如果你有span.hidden { visibility: hidden; display: inline; })。
  • “形成堆叠上下文”列表示任何使用opacity(除了opacity: 1.0;)都将创建一个新的堆叠上下文,这会使position属性的使用变得更加复杂。
  • “响应指针事件”列表示元素是否会对指针设备(如鼠标、触摸屏、触控笔等)的用户交互做出响应。
    • 例如,如果使用了visibility: hidden;,则:hover状态无法生效,点击同一元素也不会应用:focus:active
    • 此外,DOM不会触发任何指针事件,你需要在JavaScript中处理它们(例如,使用visibility: hidden;不会触发mouseclicktouchstart等事件——请注意,某些元素(如<button>)仍然可以引发click事件,如果用户使用非指针输入方法(如键盘或语音(可访问)导航方式),则可以激活该元素。
      • 你可以使用pointer-events: none;来阻止指针事件,但这不会阻止键盘和其他非指针输入,因此不应将其用

        这张表格展示了这三个属性的主要值之间更完整的比较:

        属性 是否绘制 在布局中 层叠上下文 指针事件 键盘事件 可动画化
        不透明度
        opacity: 0; 新的
        opacity: 0.1; 新的
        opacity: 0.9; 新的
        opacity: 1; 各不相同
        可见性
        visibility: hidden; 各不相同 是,有限制
        visibility: visible; 各不相同 是,有限制
        显示方式
        display: none; 各不相同
        display: contents; 仅文本和子元素 仅文本和子元素 各不相同
        其他
        pointer-events: none; N/A N/A N/A

      “Animatable”列指示该属性是否可以与CSS过渡(transition:)或CSS动画(@keyframes)一起使用。

      • 重要的是,display:属性无法进行动画处理,这就是为什么我们不能使用@keyframes时间轴在动画完成后完全隐藏元素的原因。
        • 但奇怪的是,尽管不连续,我们可以动画处理visibility:属性,虽然有限制条件
    • 此外,请勿混淆类似命名的backface-visibilitycontent-visibility属性。

      • backface-visibility仅适用于3D transform操作。
      • content-visibility是加速页面加载的优化,但需要首先使用CSS Containment,这超出了本问答的范围。

26
我应该打印这张表并将其粘在电脑显示器的边缘。 - user1663023
2
完美的答案!非常有帮助。 - Shay Yzhakov
解释无以复加 - Kushagr Arora
1
请注意,可以使用jQuery来触发不消耗点击的元素的点击。 - Bart S

18

这里找到的答案可以回答你的第一个问题(很可能是display:none,因为空间被完全折叠了)。

针对你的第二个问题,像这样的工具会对你有所帮助。 然而,40000个div看起来太多了,你可能会使用canvas或SVG获得更好的性能(例如,使用KineticJS库,因为它处理动画-变换、旋转、缩放等等)。


5
应该将该问题标记为重复,而不是链接到重复答案。 - givanse
1
http://www.kaizou.org/2011/06/effectively-measuring-browser-framerate-using-css/ 已经是404错误页面了,我已经编辑了答案,请相应修改。 - Funk Forty Niner
太棒了!我很佩服你先回答问题,然后提出更好的方法。 - Guy Park

9
如果使用display:none或visibility:hidden,性能将成为一个问题,因为它们会在大多数浏览器中触发绘图和布局,这意味着每当这两个改变时,您的浏览器都会重新绘制视口。所以我建议使用不透明度,但是对于那么多的div,它仍然不会像预期的那样具有良好的性能。您可以尝试使用WebGL,使用名为html-gl的库来在WebGL中渲染您的divs,请查看https://github.com/PixelsCommander/HTML-GL

这是正确的答案。不透明度为0可以解决大部分问题。 - evanjmg
1
为什么可见性的改变会触发布局? - gaurav5430
1
@gaurav5430 因为特定浏览器的实现方式,它知道哪些属性更改会触发绘制/布局/合成检查,请参考 https://csstriggers.com/。 - Elias Bundala
谢谢!这是一个很棒的资源。 - gaurav5430

9

display:none会隐藏整个元素并将其从布局空间中移除,而visibility:hidden会隐藏元素但仍占用与之前相同的空间。
如果您想创建透明度或淡入淡出效果,可以使用不透明度。


7
以下是已验证答案的汇编信息。
这些CSS属性都是独特的。除了使元素不可见之外,它们还具有以下额外效果:
1. 折叠元素本应占用的空间 2. 响应事件(例如点击、按键) 3. 参与tab顺序
透明度: 0 否 是 是 可见性:隐藏 否 否 否 可见性:折叠 * 否 否 显示:无 是 否 否
* 在表元素内为是,在其他情况下为否。
来源:链接

4

display:none 是因为div被取出了流,因此它们的位置不需要计算。

话虽如此,40000个div听起来很疯狂。你有考虑过像HTML5画布或SVG这样的替代方案吗?


谢谢。但是你没有回答我的第二个问题。我如何测量渲染性能,例如fps、gpu使用率? - Cihad Turhan
那是因为我没有那方面的经验。但是在快速的谷歌搜索中,一些浏览器插件出现了,你试过吗? - Fabian Schmengler
3
我找到了。使用Shift + Esc可以打开任务管理器,显示内存、CPU、FPS等信息。 - Cihad Turhan
啊,我忘了Chrome任务管理器。当然它可以测量一切 :) - Fabian Schmengler

1
有时我会同时使用可见性和透明度来实现效果,以避免点击事件。
例如:
正常状态/元素从屏幕中移除:
visibility:hidden;
opacity:0;
transition: all .3s;

屏幕上的悬停状态/元素:
visibility:visible;
opacity:1;

0

在调查Safari移动版本中的hover bug时发现了这个帖子。

确认opacity: 0是一个有效的方法(在我的情况下是这样,谢谢大家)。opacity: 0修复了足够的问题,使其可行(仍然需要在屏幕旋转[宽度变化]时进行烦人的js重绘)。

关于我用opacity: 0修复的bug的背景信息: hover位于包含一个div的li上,在悬停(或单击移动设备上的)日历条目时显示。在Safari移动版中真的很随机,有时工作,有时不工作 - 更奇怪的是,行为会随着屏幕旋转而改变++ [注意:没有涉及媒体查询,因此不是这种情况]。

如此烦人,因为在我尝试的所有其他浏览器中都正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接