javascript v-if 上的平滑 vue 折叠过渡

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/52020624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 09:47:40  来源:igfitidea点击:

Smooth vue collapse transition on v-if

javascriptcssvuejs2transitions

提问by The Wideboy

I'm struggling with vue transitions trying to show / hide content using v-if smoothly. Whilst I understand the css classes and transitions, I can make the content appear 'smoothly' using things like opacity or translation etc...but once the animation is complete (or rather as it starts), any html sections below seem to 'jump'.

我正在努力尝试使用 v-if 平滑地显示/隐藏内容的 vue 转换。虽然我了解 css 类和转换,但我可以使用不透明度或翻译等使内容“平滑”地出现……但是一旦动画完成(或者更确切地说,它开始时),下面的任何 html 部分似乎都会“跳跃” '。

I'm trying to achieve the same affect as the Bootstrap 4 'collapse' class - click one of the top buttons here: https://getbootstrap.com/docs/4.0/components/collapse/

我正在尝试实现与 Bootstrap 4 'collapse' 类相同的效果 - 单击此处的顶部按钮之一:https: //getbootstrap.com/docs/4.0/components/collapse/

As the hidden section appears / disappears, all the html content 'slides' nicely with it.

当隐藏部分出现/消失时,所有的 html 内容都可以很好地“滑动”。

Is this possible using Vue transition for content being shown using v-if? All the samples on the vue transitions docs, whilst having great css transition effects, have the below html 'jump' once the transition has started or is complete.

这是否可以使用 Vue 转换来显示使用 v-if 的内容?vue 转换文档中的所有示例,虽然具有出色的 css 转换效果,但在转换开始或完成后具有以下 html 'jump'。

I've seen some pure js solutions using max-height - https://jsfiddle.net/wideboy32/7ap15qq0/134/

我见过一些使用 max-height 的纯 js 解决方案 - https://jsfiddle.net/wideboy32/7ap15qq0/134/

and tried with vue: https://jsfiddle.net/wideboy32/eywraw8t/303737/

并尝试使用 vue:https: //jsfiddle.net/wideboy32/eywraw8t/303737/

.smooth-enter-active, .smooth-leave-active {
  transition: max-height .5s;
}
.smooth-enter, .smooth-leave-to {
  max-height: 0 .5s;
}

Thanks!

谢谢!

采纳答案by r3zaxd1

If you want to animate max-height, then you should enter the amount of max-height for the element you want to animate, also correct the second class as you put 's' (or seconds) in max-height definition :

如果要为最大高度设置动画,则应输入要设置动画的元素的最大高度量,并在将“s”(或秒)放入最大高度定义时更正第二类:

p{
  max-height: 20px;
}
.smooth-enter-active, .smooth-leave-active {
  transition: max-height .5s;
}
.smooth-enter, .smooth-leave-to {
  max-height: 0;
}

if you want something like bs4 collapse then the example inside vue website will do :

如果你想要类似 bs4 崩溃的东西,那么 vue 网站里面的例子会做:

.smooth-enter-active, .smooth-leave-active {
  transition: opacity .5s;
}
.smooth-enter, .smooth-leave-to {
  opacity: 0
}

Edit : What you are trying to do is achievable by first finding out the height of the content and then setting it inside .*-enter-toand .*-leaveclasses. One way to do that is demonstrated in fiddle below :

编辑:首先找出内容的高度,然后将其设置在内部.*-enter-to.*-leave类中,可以实现您要尝试做的事情。下面的小提琴演示了一种方法:

https://jsfiddle.net/rezaxdi/sxgyj1f4/3/

https://jsfiddle.net/rezaxdi/sxgyj1f4/3/

You can also completely forget about v-if or v-show and just hide the element using height value which I think is a lot smoother :

您也可以完全忘记 v-if 或 v-show 并使用我认为更平滑的高度值隐藏元素:

https://jsfiddle.net/rezaxdi/tgfabw65/9/

https://jsfiddle.net/rezaxdi/tgfabw65/9/

回答by Alexandr Vysotsky

i also had similar task. I found that it isn't possible to do it without JS. So i write custom transition component ( Reusable Transitions) and it works for me:

我也有类似的任务。我发现没有 JS 是不可能做到的。所以我编写了自定义转换组件(Reusable Transitions),它对我有用:

Vue.component('transition-collapse-height', {
  template: `<transition
    enter-active-class="enter-active"
    leave-active-class="leave-active"
    @before-enter="beforeEnter"
    @enter="enter"
    @after-enter="afterEnter"
    @before-leave="beforeLeave"
    @leave="leave"
    @after-leave="afterLeave"
  >
    <slot />
  </transition>`,
  methods: {
    /**
     * @param {HTMLElement} element
     */
    beforeEnter(element) {
      requestAnimationFrame(() => {
        if (!element.style.height) {
          element.style.height = '0px';
        }

        element.style.display = null;
      });
    },
    /**
     * @param {HTMLElement} element
     */
    enter(element) {
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          element.style.height = `${element.scrollHeight}px`;
        });
      });
    },
    /**
     * @param {HTMLElement} element
     */
    afterEnter(element) {
      element.style.height = null;
    },
    /**
     * @param {HTMLElement} element
     */
    beforeLeave(element) {
      requestAnimationFrame(() => {
        if (!element.style.height) {
          element.style.height = `${element.offsetHeight}px`;
        }
      });
    },
    /**
     * @param {HTMLElement} element
     */
    leave(element) {
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          element.style.height = '0px';
        });
      });
    },
    /**
     * @param {HTMLElement} element
     */
    afterLeave(element) {
      element.style.height = null;
    },
  },
});

new Vue({
  el: '#app',
  data: () => ({
    isOpen: true,
  }),
  methods: {
    onClick() {
      this.isOpen = !this.isOpen;
    }
  }
});
.enter-active,
.leave-active {
  overflow: hidden;
  transition: height 1s linear;
}

.content {
  background: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <button @click="onClick">
    open/hide
  </button>
  <transition-collapse-height>
   <div v-show="isOpen" class="content">
     <br/>
     <br/>
     <br/>
     <br/>
   </div>
  </transition-collapse-height>
</div>