使用 Transition 在 Vuejs 中的 v-if 上设置动画高度

Animate Height on v-if in Vuejs using Transition

我正在使用以下代码通过将高度从 0 像素降低来为 v-if 元素设置动画。动画效果很好。但问题是我必须指定元素的初始高度为 CSS。对于一个元素,这没问题,但我想将此动画应用于多个元素。我怎样才能解决这个问题?这样无论高度如何,动画效果都很好!

<transition name="fadeHeight" mode="out-in">
<div v-if="something">
<p>something over here where the height is not constant</p>
</div>
</transition>

.fadeHeight-enter-active,
.fadeHeight-leave-active {
  transition: all 0.2s;
  height: 230px;
}
.fadeHeight-enter,
.fadeHeight-leave-to
{
  opacity: 0;
  height: 0px;
}

您似乎没有发布所有代码,但希望我理解了目标。

尝试将过渡移动到 max-height 属性:

.fadeHeight-enter-active,
.fadeHeight-leave-active {
  transition: all 0.2s;
  max-height: 230px;
}
.fadeHeight-enter,
.fadeHeight-leave-to
{
  opacity: 0;
  max-height: 0px;
}

只要您将最大高度设置为大于最高元素,它应该可以满足您的需要。请注意,您可能还想使用 overflow:hidden。如果元素的实际高度有显着变化,这个解决方案可能不是最好的,因为它会使动画 duration/delay 看起来非常不同。

https://jsfiddle.net/7ap15qq0/4/

@ryantdecker 提供了最常见的答案。不过,我更喜欢做更少的代码,而是做 class binding

<template>
 <!-- isShowing either a data or computed... -->
 <div class="foo" :class="{'showing': isShowing', 'hidden': !isShowing}">
  <p>
   something over here where the height is not constant
  </p>
 </div>
</template>
...
<style>
.foo {
 height: auto;
 transition: max-height 0.5s;
 &.showing {
  max-height: 200px; //MUST BE MORE THAN height:auto
 }
 &.hidden {
  max-height: 0px;
 }
}
</style>

可以进行一些自定义以获得更多控制:

  1. 设置:style="{'max-height': computedHeight}"
  2. .showing.hidden class 中分别使用 ease-inease-out 以及两个不同的 transitions
  3. 对 collapse/expand
  4. 的超长内容使用三次贝塞尔曲线过渡速度

当您使用不同的项目时,可以使用上面的第一个自定义项,例如图片、弹性行,其中可以通过开发工具查看高度并计算高度。例如:

computed: {
 /**
  * @return {string} max height of the container in pixels if shown else zero
  */
 calcedHeight()
 {
   const elHeight = 80;
   const maxHeight = this.isShowing ? elHeight * this.elementCount : 0
   const maxHeightPx = maxHeight + 'px'
   return {
    'max-height': maxHeightPx
   }
 }
}

此时可以很容易地将其制作成具有 isShowingelHeightelCount 属性的组件。

三次贝塞尔曲线

我将其作为自己的部分,因为对于疯狂的长元素(想想 5000 像素 max-heights),它可能是所有需要的部分):

&.showing {                                                                                          
   transition: all 0.6s cubic-bezier(1, 0.01, 1, 0.01);                                                 
}                                                                                                       
&.hidden {                                                                                           
   transition: all 0.6s cubic-bezier(0.01, 1, 0.01, 1);                                                 
}

如前所述 - 最大高度过渡通常是解决此问题的方法。 但在某些情况下,您可能无法使用最大高度过渡。 对于这些情况,您可以使用包装器容器组件,它会在需要时进行转换。

<template>
  <div
    class="fluid-wrapper"
    :class="{ 'in-transition': transitionState }"
    :style="computedDimensions"
    @transitionend="transitionState = 0"
  >
    <slot />
  </div>
</template>
<script>
export default {
  name: 'FluidContainer',
  props: ['trigger'],
  data() {
    return {
      oldRect: {
        height: null,
        width: null,
      },
      newRect: {
        height: null,
        width: null,
      },
      transitionState: 0,
      // 0: no Dimensions, no transition
      // 1: oldRect Dimensions, transition is on
      // 2: newRect Dimensions, transition is on
    };
  },
  computed: {
    computedDimensions() {
      if (!this.transitionState) {
        return null;
      }
      return this.transitionState === 1 ? this.oldRect : this.newRect;
    },
    dimensionsHasChanged() {
      return (
        this.newRect.height !== this.oldRect.height
        || this.newRect.width !== this.oldRect.width
      );
    },
  },
  watch: {
    trigger() {
      const oldStyle = getComputedStyle(this.$el);
      this.oldRect.height = oldStyle.height;
      this.oldRect.width = oldStyle.width;
      this.$nextTick(() => {
        const newStyle = getComputedStyle(this.$el);
        this.newRect.height = newStyle.height;
        this.newRect.width = newStyle.width;
        if (this.dimensionsHasChanged) {
          this.transitionState = 1;
          window.requestAnimationFrame(() => {
            this.transitionState = 2;
          });
        } else {
          this.transitionState = 0;
        }
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.fluid-wrapper {
  /* overflow: hidden; */
  height: fit-content;
  width: fit-content;
  &.in-transition {
    transition: all 0.3s;
  }
}
</style>

用法:

<FluidContainer :trigger="some-variable">
    <!-- Any Reactive Content -->
</FluidContainer>

'trigger' 道具 - 这是工作所必需的。 它可以是使内部内容发生变化的任何状态。 包装器将监视触发器以检测尺寸何时发生变化并进行转换。