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

Posted

技术标签:

【中文标题】在Vuejs中使用Transition在v-if上设置高度动画【英文标题】:Animate Height on v-if in Vuejs using Transition 【发布时间】: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;

【问题讨论】:

【参考方案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>

可以进行更多控制的一些自定义是:

    设置:style="'max-height': computedHeight".showing.hidden 类中分别使用ease-inease-out 和两个不同的transitions。 对折叠/展开的超长内容使用三次贝塞尔曲线过渡速度

当您使用不同的项目时,可以使用上面的第一个自定义项,例如图片、可以通过 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' 道具 - 需要它才能工作。 它可以是使内部内容发生变化的任何状态。 包装器将监视触发器以检测何时发生尺寸变化并进行转换。

【讨论】:

以上是关于在Vuejs中使用Transition在v-if上设置高度动画的主要内容,如果未能解决你的问题,请参考以下文章

VueJS:在 v-if 中直接改变 prop 与 this.[prop]

VueJS复合v-if

(transition动画组件)

vuejs中类似于v-if的自定义指令

VueJS v-if

如何在VueJS中使用$ http.delete删除html元素