【问题标题】:Why is this animation incorrect?为什么这个动画不正确?
【发布时间】:2019-06-21 16:41:28
【问题描述】:

我正在尝试使用隐藏的行来实现一个表(目前基于 CSS-grid)。单击某处后,该行应平滑扩展。我希望这一行的内容在动画期间可见并保持正确的大小

这与this demo 非常相似,但我没有实现菜单。但在那个演示中,内容(“菜单项 1”、“菜单项 2”……)在动画期间具有恒定大小的事实是我想要的。

我想使用FLIP technique as described here 来实现这一点,以轻松实现高帧率。

我在行上添加了scaleY(0.01),在内部包装上添加了scaleY(100),希望它们能够抵消并保持规模。我还添加了transform-origin: 0 0,希望动画行的上边缘在动画期间保持在同一位置。

但是,会发生的情况是内容最初太高了。即使我设置了transform-origin: 0 0,它们似乎也沿 Y 轴移动(但这可能只是高度不正确的影响)。

我尝试使用Element.animate() 以及手动element.style.transform = ... 方法,但无济于事。

我的问题:为什么隐藏行的动画效果不正确(其内容的高度不是恒定的,它们似乎沿 Y 轴移动)?如何解决这个问题?

let collapsed = Array.from(document.querySelectorAll(".collapsed"));

collapsed.forEach((row, idx) => {
  row.dataset["collapsedIdx"] = idx;
  document.querySelector("label").addEventListener("click", () => {
    let collapsedSelf = row.getBoundingClientRect();
    let rowsBelow = Array.from(
      row.parentElement.querySelectorAll(`[data-collapsed-idx='${idx}'] ~ *`)
    );

    row.classList.remove("collapsed");
    let expandedSelf = row.getBoundingClientRect();
    let diffY = expandedSelf.height - collapsedSelf.height;

    let animationTiming = {
      duration: 2000,
      easing: "linear"
    };

    let wrapper = row.querySelector(":scope > *");

    row.animate(
      [{ transform: `scaleY(0.01)` }, { transform: `scaleY(1)` }],
      animationTiming
    );

    wrapper.animate(
      [{ transform: `scaleY(100) ` },
       { transform: `scaleY(1) ` }],
      animationTiming
    );

    rowsBelow.forEach((rowBelow) => {
      rowBelow.animate([
        { transform: `translateY(-${diffY}px)` },
        { transform: `translateY(0)` }
      ], animationTiming);
    });
  });
});
* {
  box-sizing: border-box;
}

section {
  display: grid;
  grid-template-columns: max-content 1fr;
}

.subsection {
  grid-column: span 2;
}

label, p {
  display: block;
  margin: 0;
}

.collapsible,
.collapsible > * {
  transform-origin: 0 0;
  will-change: transform;
  contain: content;
}

.collapsed {
  height: 0;
  overflow: hidden;
}

/* .collapsed {
  transform: scaleY(.2);
}

.collapsed > * {
  transform: scaleY(5)
}

*:nth-child(8),
*:nth-child(9),
*:nth-child(10),
*:nth-child(11),
*:nth-child(12) { transform: translateY(-35px); }

  */

/* .animate-on-transforms {
  transition: transform 2000ms linear;
} */
<section>
    <label><strong>CLICK ME!!!</strong></label>
    <p>Value 1</p>

    <label>Row 2</label>
    <p>Value 2</p>

    <label>Row 3</label>
    <p>Value 3</p>
    <div class="subsection collapsible collapsed">
      <section>
        <label>Subrow 1</label>
        <p>Subvalue 1</p>

        <label>Subrow 2</label>
        <p>Subvalue 2</p>

        <label>Subrow 3</label>
        <p>Subvalue 3</p>

        <label>Subrow 4</label>
        <p>Subvalue 4</p>

      </section>
    </div>

    <label>Row 4</label>
    <p>Value 4</p>

    <label>Row 5</label>
    <p>Value 5</p>

</section>

【问题讨论】:

  • 呃,你为什么需要Javascript?我认为这可以完全用 CSS3 完成
  • @Martin 可以吗?我想我需要为 heightmax-height 制作动画,这不能保证在较慢的机器上达到 60 FPS。这就是我尝试 FLIP 技术的原因。
  • 在我看来,您的动画效果似乎有误。您是否有显示正确/所需动画的原型,即使性能不如 JS 版本?比如... 1 个元素?
  • 我手头没有我的确切代码的替代实现@AndreiGheorghiu,但我可能会使用常见的max-height 技术,比如这里:codepen.io/brundolf/pen/bqqeye
  • 我正在考虑回答您的问题,但我想确保我首先了解预期的结果。所以你只是想要一个平滑的抽屉效果?没有缩放、3d 旋转之类的东西?

标签: css animation css-transitions css-transforms


【解决方案1】:

我认为这里的问题是插值。您假设在动画的每个步骤中,我们将始终拥有 scale(x)scale(1/x),其中 x[0,1] 内,但由于四舍五入,我认为您不会拥有这个。

这是一个基本的例子:

.box,
p{
 transform-origin:0 0;
 transition:1s linear;
}
.box:hover {
  transform:scale(0.1);
}
.box:hover p{
  transform:scale(10);
}
<div class="box">
<p>
Lorem Ipsume<br>
Lorem Ipsume<br>
Lorem Ipsume<br>
Lorem Ipsume<br>
Lorem Ipsume<br>
</p>
</div>

从逻辑上讲,我们可能认为文本会保持不变,但不会。它会增长然后再次缩小。所以是的,比例会被取消,但不会沿着动画。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    • 2013-02-03
    相关资源
    最近更新 更多