如何使 CSS 变换影响其他元素的流动

How to make a CSS transform affect the flow of other elements

我使用 CSS 转换和 transform 属性 来在添加和删除元素时缩小元素。

但是这样的一个问题是这个属性不影响其他元素的流动,所以看起来好像被删除的元素缩小了,然后其余的元素突然跳了起来。

如果我要为高度设置动画 属性 而不是使用变换,这会很好,但是在实际使用中我使用的是可变高度的元素,所以我不知道我可以在什么高度之间设置动画.


编辑: 人们建议为 height 属性(如上所述不起作用)或 max-height 设置动画属性。 max-height 属性 会在某种程度上起作用,但是您无法完美地对齐时间,因为过渡会不断调整 max-height 属性 超过元素的实际高度,直到过渡时间结束。

这些方法的另一个问题是它没有使用您可以通过 transform 属性 实现的流畅动画。对象的变换将顺利发生,但是随着浏览器以不同的方式呈现这些过渡,以下元素的移动会断断续续。


这是一个 JSFiddle,我的意思是(尝试添加然后删除元素,并在删除元素时查看跳转):

var button = document.querySelector("button");
var box = document.createElement("div");

box.className = "box";
box.appendChild(document.createTextNode("Click to delete"));

button.addEventListener("click", function(e) {
  var new_box = box.cloneNode(true);

  new_box.addEventListener("click", function(e) {
    this.className = "box deleting";
    window.setTimeout(function(e) {
      new_box.remove();
    }, 1000);
  });

  this.parentNode.appendChild(new_box);
});
button {
  font-size: 20pt;
}
.box {
  font-size: 20pt;
  margin: 10px;
  width: 200px;
  padding: 10px;
  background: pink;
  transform: scale(1, 1);
  transform-origin: top left;
}
.deleting {
  transform: scale(1, 0);
  transition: all 1000ms ease;
}
<button>
  Add Box
</button>

您可以在收缩框周围创建一个包装纸。现在你只是缩放红色框,这意味着它仍然使用相同的 space,但它是以缩放的方式呈现的。当你最后删除它时,它不再使用那个space,它下面的元素会向上跳。

如果您在每个红色框周围创建一个包装器,您可以控制它需要的 space。 只需更改包装器的高度,也可以进行转换。

/* css for a wrapper */
overflow: hidden;
transition: height .2s; /* your time */

因此,不仅要用过渡缩放红色框,还要将它们放在包装元素中,并更改该元素的高度。

为了获得最佳效果,您需要修改影响其周围环境的装箱属性。这包括 width/height、填充、边距和边框宽度。在下面的示例中,我将相关属性设置为 0 以获得您想要的效果。这包括影响元素垂直间距的所有属性:heightpadding-[top/bottom]margin-[top/bottom]border-[top/bottom]-width 都转换为 0。

我还在方框中添加了 box-sizing: border-box;overflow: hidden; - 你应该看看如果没有设置它们会发生什么。我会让你自己了解这些。

var button = document.querySelector("button");
var box = document.createElement("div");

box.className = "box";
box.appendChild(document.createTextNode("Click to delete"));

button.addEventListener("click", function(e) {
  var new_box = box.cloneNode(true);

  new_box.addEventListener("click", function(e) {
    new_box.style.height = this.offsetHeight + 'px';
    window.requestAnimationFrame(function () {
      new_box.style.height = 0;
      new_box.className = "box deleting";
      window.setTimeout(function(e) {
        new_box.remove();
      }, 1000);
    });
  });

  this.parentNode.appendChild(new_box);
});
button {
  font-size: 20pt;
}
.box {
  box-sizing: border-box;
  overflow: hidden;
  font-size: 20pt;
  margin: 10px;
  width: 200px;
  padding: 10px;
  border: 5px solid red;
  background: pink;
  transform: scale(1, 1);
  transform-origin: top left;
}
.deleting {
  height: 0;
  padding-top: 0;
  padding-bottom: 0;
  margin-top: 0;
  margin-bottom: 0;
  border-top-width: 0;
  border-bottom-width: 0;
  transform: scale(1, 0);
  transition: all 1000ms ease;
}
<button>
  Add Box
</button>

For elements whose layout is governed by the CSS box model, the transform property does not affect the flow of the content surrounding the transformed element.

REF:Transform Rendering

您必须使用 max-height 属性 (check this answer) 才能获得所需的效果。

var button = document.querySelector("button");
var box = document.createElement("div");

box.className = "box";
box.appendChild(document.createTextNode("Click to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to deleteClick to delete"));

button.addEventListener("click", function(e) {
  var new_box = box.cloneNode(true);
  new_box.style.height = ( Math.random() * (200 - 30) + 30 ) + 'px';

  new_box.addEventListener("click", function(e) {
    this.className = "box deleting";
    window.setTimeout(function(e) {
      new_box.remove();
    }, 1000);
  });

  this.parentNode.appendChild(new_box);
});
button {
  font-size: 20pt;
}
.box {
  overflow:hidden;
  font-size: 12pt;
  margin-bottom: 10px;
  width: 600px;
  max-height:1000px;
  padding: 10px;
  background: pink;
  transform: scaleY(1);
  transform-origin: top left;
}
.deleting {
  transform: scaleY(0);
  max-height:0;
  padding:0 10px;
  margin-bottom:0;
  transition: padding 1000ms ease,max-height 1000ms ease, transform 500ms ease 500ms, margin-bottom 1000ms ease;
}
<button>
  Add Box
</button>

没有办法做到正确(单独 css)¬¬

我的做法是部分使用 JS(在我的例子中是 C#/Blazor)

我使用了 max-height 但我将其设置为 var(),并且我根据我拥有的内容计算的值本身(我使用的是 JS/c # 为此),然后使用该值通过 css 变量设置 inline style

<ul 
    class="submenu @SubMenuActiveClass" 
    style="--max-height:@((int)(40.3f * Model.Children.Count))px">

    <!--... childrens with 40.3px height each -->

</ul>
.submenu {
    max-height: 0;
    transition: max-height 0.3s ease-out;
    overflow: hidden;

    &.active {
        max-height: var(--max-height);
    }
}

示例在 Blazor 和 SCSS 中,但很容易理解