【问题标题】:Animate Height on v-if in Vuejs using Transition在Vuejs中使用Transition在v-if上设置高度动画
【发布时间】:2017-07-24 07:09:56
【问题描述】:

我正在使用以下代码通过将高度降低到 0px 来为 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;
}

【问题讨论】:

    标签: css animation vuejs2 vue.js


    【解决方案1】:

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

    尝试将过渡移动到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。如果元素的实际高度有显着变化,则此解决方案可能不是最佳解决方案,因为它会使动画持续时间/延迟显得非常不同。

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

    【讨论】:

    • 这不是一个干净的动画,不应该是接受者的答案,这只是一个 CSS hack。设置更高的 max-height 值会以一种方式延迟动画并减少另一种方式的过渡时间。
    • 完全同意它并不完美,但它不是黑客——它直接回答了被问到的问题。答案已经指出了效果的潜在问题/变化,具体取决于相对于使用的最大高度值的大小。如果您正在寻找不同的东西,这里有一些替代方案和优缺点的详细分类:css-tricks.com/using-css-transitions-auto-dimensions
    • 没有必要使用最大高度方法的过渡组件。因为它只贡献更多代码。
    【解决方案2】:

    @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 类中分别使用ease-inease-out 和两个不同的transitions
    3. 对折叠/展开的超长内容使用三次贝塞尔曲线过渡速度

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

    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 属性将其制成组件。

    三次贝塞尔曲线

    我给这个它自己的部分,因为它可能是关于疯狂的长元素所需要的(想想 5000px 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);                                                 
    }
    

    【讨论】:

      【解决方案3】:

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

      <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' 道具 - 需要它才能工作。 它可以是使内部内容发生变化的任何状态。 包装器将监视触发器以检测何时发生尺寸变化并进行转换。

      【讨论】:

        猜你喜欢
        • 2020-12-19
        • 2019-01-03
        • 1970-01-01
        • 2017-11-02
        • 2018-07-30
        • 1970-01-01
        • 2019-07-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多