Vue.js过渡CSS仅向下滑动

问题描述

我一直在寻找解决此问题的方法很长时间,但似乎找不到最佳的解决方案。基本上,我有一个手风琴,当用户打开它时,它应该在平稳过渡的情况下轻轻滑下。

我很确定只有CSS才有可能。如果我错了,请纠正我,但是CSS过渡在性能上比JS过渡要好。

因此,现在我所面临的问题的HTML:

<transition name="slide-down">
  <ul v-if="isdisplayingopeningHours" class="mt-2 text-sm flex flex-col space-y-2">
    <li v-for="i in 10" :key="i"> <!-- just for testing purposes -->
      element
    </li>
  </ul>
</transition>

css是:

.slide-down-enter-active,.slide-down-leave-active {
  transition: all .5s;
}

.slide-down-enter-to,.slide-down-leave {
  overflow: hidden;
  max-height: 100%;
}

.slide-down-enter,.slide-down-leave-to {
  overflow: hidden;
  max-height: 0;
}

这导致过渡不流畅。向下滑动似乎不是问题,但是当向上滑动时,它确实很不整洁,一点也不流利!如果我设置max-height: 10rem并不能改善它。我希望有人可以通过调试帮助解决这个问题,甚至可以使我更多地了解vue转换以及js vs css转换。

修改
为了澄清起见,过渡本身仅应使用CSS。手风琴的开头是用js,这很好。也许还会看到一个与本地vue-transition组件一起使用过渡的示例。

编辑2:
这是一个codesandBox,它也有同样的问题。 https://codesandbox.io/s/frosty-bash-lmc2f?file=/src/components/HelloWorld.vue

解决方法

另一个更新! 为了实现JS过渡,您可以看看official document进行学习。无论如何,我不会实现花哨的计时机制,但是以最简单的形式,react中的JS崩溃过渡将类似于以下内容,我将其包含在现有的codesandbox中:

<template>
...
  <div>
    <button @click="jsOpen = !jsOpen">Open JS list</button>
    <transition
      v-on:before-enter="beforeEnter"
      v-on:enter="enter"
      v-on:after-enter="afterEnter"
      v-on:enter-cancelled="enterCancelled"
      v-on:before-leave="beforeLeave"
      v-on:leave="leave"
      v-on:after-leave="afterLeave"
      v-on:level-cancelled="leaveCancelled"
      v-bind:css="false"
    >
      <ul v-if="jsOpen" class="mt-2 text-sm flex flex-col space-y-2">
        <li v-for="i in 10" :key="i">item-{{ i }}</li>
      </ul>
    </transition>
  </div>
</template>

<script>
export default {
  ...
  methods: {
    beforeEnter(el) {
      el.style.height = 0;
      el.style.overflow = "hidden";
    },enter(el,done) {
      const increaseHeight = () => {
        if (el.clientHeight < el.scrollHeight) {
          const height = `${parseInt(el.style.height) + 5}px`;
          el.style.height = height;
        } else {
          clearInterval(this.enterInterval);
          done();
        }
      };
      this.enterInterval = setInterval(increaseHeight,10);
    },afterEnter(el) {},enterCancelled(el) {
      clearInterval(this.enterInterval);
    },beforeLeave(el) {},leave(el,done) {
      const decreaseHeight = () => {
        if (el.clientHeight > 0) {
          const height = `${parseInt(el.style.height) - 5}px`;
          el.style.height = height;
        } else {
          clearInterval(this.leaveInterval);
          done();
        }
      };
      this.leaveInterval = setInterval(decreaseHeight,afterLeave(el) {},leaveCancelled(el) {
      clearInterval(this.leaveInterval);
    },},};
</script>

更新: 在了解了OP的要求之后,我尝试着做些不同的事情,偶然发现this article on css-tricks解释了关于过渡的更多细节,从那篇文章中提出的另一个非js解决方案看起来像是下面的内容,我将其包括在现有的stackblitz

.scale-enter-active,.scale-leave-active {
  transform-origin: top;
  transition: transform 0.3s ease-in-out;
}

.scale-enter-to,.scale-leave-from {
  transform: scaleY(1);
}

.scale-enter-from,.scale-leave-to {
  transform: scaleY(0);
}

此处代替高度,而是沿y轴缩放。 IT还具有一个副作用,即首先将容器扩展到完整大小,然后再更新(缩放)内容。

Vue v3.x V3中的-leave-enter类被重命名为-leave-from-enter-from。看看这个stackblitz

.slidedown-enter-active,.slidedown-leave-active {
  transition: max-height 0.5s ease-in-out;
}

.slidedown-enter-to,.slidedown-leave-from {
  overflow: hidden;
  max-height: 1000px;
}

.slidedown-enter-from,.slidedown-leave-to {
  overflow: hidden;
  max-height: 0;
}

Vue v2.x 问题是您将max-height指定为100%,请使用比预期的最大高度更大的值。将您的样式更新为:

.slide-down-enter-active,.slide-down-leave-active {
  transition: max-height 0.5s ease-in-out;
}

.slide-down-enter-to,.slide-down-leave {
  overflow: hidden;
  max-height: 1000px;
}

.slide-down-enter,.slide-down-leave-to {
  overflow: hidden;
  max-height: 0;
}

,它应该可以工作。看看这个codesandbox演示。