问题描述
我一直在寻找解决此问题的方法很长时间,但似乎找不到最佳的解决方案。基本上,我有一个手风琴,当用户打开它时,它应该在平稳过渡的情况下轻轻滑下。
我很确定只有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演示。