【问题标题】:How to make a CSS transform affect the flow of other elements如何让 CSS 变换影响其他元素的流动
【发布时间】:2017-01-04 04:47:21
【问题描述】:

我正在使用带有 transform 属性的 CSS 过渡来在添加和删除元素时缩小它们。

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

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


编辑: 有人建议为 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>

【问题讨论】:

    标签: javascript css css-transitions css-transforms


    【解决方案1】:

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

    如果您在每个红色框周围创建一个包装器,您可以控制它占用的空间。 只需更改包装器的高度,也可以使用过渡。

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

    所以,不要只用过渡来缩放红色框,还要将它们放在一个环绕元素中,并更改该元素的高度。

    【讨论】:

      【解决方案2】:

      为了获得最佳效果,您需要修改影响其周围环境的拳击属性。这包括宽度/高度、填充、边距和边框宽度。在下面的示例中,我将相关属性设置为 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>

      【讨论】:

      • 嗨瑞恩,感谢您的回答。这适用于固定高度的元素,但不适用于可变高度的元素。
      • 谢谢@Lauren - 我想用按钮,固定高度没有太大危害。您没有要求通用解决方案,但您是绝对正确的-我没有在原始答案中提及固定高度要求。我已将其更新为不再需要固定高度。
      • 另外值得一提的是,当您开始将动画附加到您的应用程序状态时,您可以通过混合 css 动画很快陷入困境。有各种各样的时间问题、优先顺序问题和[最糟糕的]维护问题。例如,如果您希望持续时间为 500 毫秒,您必须在两个地方更改它 - 不好。 10 年后我发现这些类型的动画应该只在 JavaScript 中完成。 CSS 动画适用于与您的应用程序状态无关的真正简单的事情。
      • 这个解决方案肯定更有希望,但由于您在后续评论中列出的原因,可能不实用。为了简单起见,我举了一个纯 JS 的例子,实际上这种情况是在一个更大的应用程序中,在其中注入与对象属性混淆的动画帧回调并非易事。 (你可能是对的,我应该研究纯 JS 解决方案)。
      • 你用你的应用做出的决定不应该影响我是否回答了你的问题;)动画框架做各种各样的疯狂,在你的对象上设置属性,正如你所说的“注入动画帧”,还有更多 - 没有办法绕过它,他们只是为你抽象它。它们中的大多数都提供了一个 API,用于在动画完成后“重置”任何副作用。没有 JS 就没有办法做你想做的事,听起来很像你需要一个动画框架。我推荐 GSAP。
      【解决方案3】:

      对于布局受 CSS 框模型控制的元素, transform 属性不影响周围内容的流动 转换后的元素。

      参考号: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>

      【讨论】:

      • 不反对,但你测试过你的答案吗?充其量只是简陋。
      • @RyanWheale 这实际上适用于具有已知高度范围的元素,例如 500px 到 800px,问题是动画时间将取决于应用的最大高度。只是想指出这种可能性:)
      • 好的,我做了一些修改来演示解决方案!但同样,这只适用于某些场景:)
      • 嗨@sabithpocker - 你的解决方案似乎运作良好。您能否为我(或在答案中)解释为什么您对max-height 使用不同的转换持续时间以及transform 转换延迟 500 毫秒的原因?
      • @Lauren 如果没有延迟转换转换最初发生并强制高度保持恒定直到转换结束,蛮力想法只是试图延迟,以便从一开始就发生高度转换。尝试消除延迟,它将为 scaleY 设置动画,然后为高度设置动画,而不是同时设置。所有值都需要调整以满足您的要求!
      【解决方案4】:

      没有办法做到正确(仅使用 css)¬¬

      我这样做的方式是部分使用 JS(在我的例子中是 C#/Blazor)

      我使用了max-height,但我使用var() 设置了它,我根据我拥有的内容计算了值本身(我为此使用了 JS/c#),然后使用该值使用 css 变量设置 内联 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,但很容易理解

      【讨论】:

        猜你喜欢
        • 2012-05-30
        • 1970-01-01
        • 2020-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-16
        • 2015-02-19
        相关资源
        最近更新 更多