CSS 显示属性上的过渡

我目前正在设计 CSS“巨型下拉菜单”- 基本上是一个常规的仅 CSS 下拉菜单,但其中包含不同类型的内容。

目前, 似乎 CSS 3 过渡不适用于'display' 属性 ,即,您不能执行从display: nonedisplay: block (或任何组合)的任何过渡。

当有人将鼠标悬停在顶层菜单项之一上时,是否可以通过上述示例使第二层菜单 “淡入”?

我知道您可以在visibility:属性上使用过渡,但是我想不出一种有效使用它的方法。

我也尝试过使用高度,但是那不幸地失败了。

我还知道使用 JavaScript 实现此功能很简单,但是我想挑战一下自己仅使用 CSS,而且我想说的还有点不足。

答案

您可以连接两个或更多个转换,这时visibility很方便。

div {
  border: 1px solid #eee;
}
div > ul {
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.5s linear;
}
div:hover > ul {
  visibility: visible;
  opacity: 1;
}
<div>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
  </ul>
</div>

(不要忘记transition属性的供应商前缀。)

更多的细节在这篇文章

您需要通过其他方式隐藏该元素才能使其正常工作。

我通过绝对定位两个<div>并将隐藏的一个设置为opacity: 0来实现效果。

如果您甚至将display属性从none切换为block ,则不会在其他元素上进行过渡。

要解决此问题,请始终允许display: block元素display: block ,但通过调整以下任何一种方式隐藏元素:

  1. height设置为0
  2. opacity设置为0
  3. 将元素放置在另一个发生overflow: hidden元素的框架之外overflow: hidden

可能还有更多解决方案,但是如果将元素切换为display: none ,则无法执行过渡display: none 。例如,您可以尝试尝试以下操作:

div {
    display: none;
    transition: opacity 1s ease-out;
    opacity: 0;
}
div.active {
    opacity: 1;
    display: block;
}

但是,这行不通的。根据我的经验,我发现这无济于事。

因此,您将始终需要保持元素display: block - 但您可以通过执行以下操作来解决它:

div {
    transition: opacity 1s ease-out;
    opacity: 0;
    height: 0;
    overflow: hidden;
}
div.active {
    opacity: 1;
    height: auto;
}

在撰写本文时,如果您尝试更改display属性,所有主要的浏览器都会禁用 CSS 转换,但是 CSS 动画仍然可以正常工作,因此我们可以将其用作解决方法。

示例代码 (您可以将其相应地应用于菜单) Demo

将以下 CSS 添加到样式表:

@-webkit-keyframes fadeIn {
    from { opacity: 0; }
      to { opacity: 1; }
}
@keyframes fadeIn {
    from { opacity: 0; }
      to { opacity: 1; }
}

然后将fadeIn动画应用于父悬停上的子项(当然设置为display: block ):

.parent:hover .child {
    display: block;
    -webkit-animation: fadeIn 1s;
    animation: fadeIn 1s;
}

更新 2019 - 该方法还支持淡出:

(需要一些 JavaScript 代码)

// We need to keep track of faded in elements so we can apply fade out later in CSS
document.addEventListener('animationstart', function (e) {
  if (e.animationName === 'fade-in') {
      e.target.classList.add('did-fade-in');
  }
});

document.addEventListener('animationend', function (e) {
  if (e.animationName === 'fade-out') {
      e.target.classList.remove('did-fade-in');
   }
});
div {
    border: 5px solid;
    padding: 10px;
}

div:hover {
    border-color: red;
}

.parent .child {
  display: none;
}

.parent:hover .child {
  display: block;
  animation: fade-in 1s;
}

.parent:not(:hover) .child.did-fade-in {
  display: block;
  animation: fade-out 1s;
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fade-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
<div class="parent">
    Parent
    <div class="child">
        Child
    </div>
</div>

我怀疑如果更改display就禁用过渡的原因是由于实际显示。它不会改变任何东西,可以令人信服地平滑的动态。

display: none;visibility: hidden;是两个完全不同的东西。
两者的作用都是使元素不可见,但具有visibility: hidden;visibility: hidden;它仍然呈现的布局,但只是没有那么明显
隐藏元素仍然占用空间,并且仍以内联方式或作为块,块内联方式或表或任何display元素指示其呈现方式的内容呈现,并相应地占用空间。
其他元素不会自动移动以占据该空间。隐藏元素只是不将其实际像素渲染到输出中。

display: none另一方面, display: none实际上不会阻止元素完全呈现。
它不占用任何布局空间。
现在,本来会占用该元素所占用的部分或全部空间的其他元素也会进行调整,以占据该空间,就像该元素根本不存在一样

display不仅仅是另一个视觉属性。
它建立元素的整个呈现模式,例如它是blockinlineinline-blocktabletable-rowtable-celllist-item还是其他任何东西!
每一个都有不同的布局效果,并且没有合理的方法来动画化或平滑地过渡它们(例如,尝试想象从blockinline的平滑过渡,反之亦然!)。

这就是为什么如果显示发生更改,过渡将被禁用的原因(即使更改是none显示或none更改 - none更改都不只是隐身,而是它自己的元素渲染模式,这意味着根本不会渲染!)。

display不是进行过渡的属性之一。

有关可以应用过渡的 CSS 属性列表,请参见Animatable CSS 属性 。请参阅CSS 值和单位模块级别 4,组合值:插值,加法和累加,以了解如何进行插值。

9.1 中最多列出了 CSS 3 。 CSS 中的属性 (仅关闭警告弹出窗口)

我也尝试过使用高度,但是那不幸地失败了。

上次必须执行此操作时,我改用了max-height ,它是一个可设置动画的属性(尽管有点 hack,但确实起作用了),但请注意,对于复杂的页面或用户访问量较低的用户而言,它可能非常麻烦端移动设备。

代替使用 CSS 中不存在的回调,我们可以使用transition-delay属性。

#selector {
    overflow: hidden; // Hide the element content, while height = 0
    height: 0; opacity: 0;
    transition: height 0ms 400ms, opacity 400ms 0ms;
}
#selector.visible {
    height: auto; opacity: 1;
    transition: height 0ms 0ms, opacity 600ms 0ms;
}

那么,这是怎么回事?

  1. 添加visible类时, heightopacity开始动画而没有延迟(0 毫秒),尽管height需要 0 毫秒才能完成动画(相当于display: block ),而不opacity需要 600 毫秒。

  2. 删除visible类后, opacity开始动画(延迟为 0 毫秒,持续时间为 400 毫秒),高度等待 400 毫秒,然后立即(0 毫秒)恢复初始值(相当于display: none动画回调中display: none值)。

请注意,这种方法比使用visibility方法更好。在这种情况下,元素仍然占据页面上的空间,并且并不总是适合。

有关更多示例,请参阅本文

您现在可以将自定义动画添加到 block 属性。

@keyframes showNav {
  from {opacity: 0;}
  to {opacity: 1;}
}
.subnav-is-opened .main-nav__secondary-nav {
  display: block;
  animation: showNav 250ms ease-in-out both;
}

演示版

在此演示中,子菜单从display:none更改为display:block并且仍然设法淡出。

根据W3C 工作草案 2013 年 11 月 19 日, display内容不是可动画制作的财产 。幸运的是, visibility是可动画的。您可以将其过渡与不透明度过渡( JSFiddle链接在一起

  • HTML:

    <a href="http://example.com" id="foo">Foo</a>
    <button id="hide-button">Hide</button>
    <button id="show-button">Show</button>
  • CSS:

    #foo {
        transition-property: visibility, opacity;
        transition-duration: 0s, 1s;
    }
    
    #foo.hidden {
        opacity: 0;
        visibility: hidden;
        transition-property: opacity, visibility;
        transition-duration: 1s, 0s;
        transition-delay: 0s, 1s;
    }
  • 用于测试的 JavaScript:

    var foo = document.getElementById('foo');
    
    document.getElementById('hide-button').onclick = function () {
        foo.className = 'hidden';
    };
    
    document.getElementById('show-button').onclick = function () {
        foo.className = '';
    };

请注意,如果仅使链接透明而不设置visibility: hidden ,则它将保持可单击状态。

我的 JavaScript 巧妙技巧是将整个场景分为两个不同的功能

为了准备事情,声明了一个全局变量并定义了一个事件处理程序:

var tTimeout;
  element.addEventListener("transitionend", afterTransition, true);//firefox
  element.addEventListener("webkitTransitionEnd", afterTransition, true);//chrome

然后,当隐藏元素时,我使用如下代码:

function hide(){
  element.style.opacity = 0;
}

function afterTransition(){
  element.style.display = 'none';
}

为了重新显示该元素,我正在执行以下操作:

function show(){
  element.style.display = 'block';
  tTimeout = setTimeout(timeoutShow, 100);
}

function timeoutShow(){
  element.style.opacity = 1;
}

到目前为止,一切正常!

编辑:在此示例中,不显示任何内容。

@keyframes hide {
  0% {
    display: block;
    opacity: 1;
  }
  99% {
    display: block;
  }
  100% {
    display: none;
    opacity: 0;
  }
}

上面发生的是,在不透明度逐渐消失的情况下,通过 99%的动画显示被设置为阻止。最后一刻,display 属性设置为 none。

最重要的一点是使用 animation-fill-mode 保留动画结束后的最后一帧:

.hide {
   animation: hide 1s linear;
   animation-fill-mode: forwards;
}

这是两个示例: https : //jsfiddle.net/qwnz9tqg/3/