88 lines
2.1 KiB
Vue
88 lines
2.1 KiB
Vue
<!-- Thanks: https://markus.oberlehner.net/blog/transition-to-height-auto-with-vue/ -->
|
|
|
|
<script>
|
|
import { Transition } from 'vue'
|
|
|
|
export default defineComponent({
|
|
name: 'TransitionExpand',
|
|
setup(_, { slots }) {
|
|
const onEnter = element => {
|
|
const width = getComputedStyle(element).width
|
|
|
|
element.style.width = width
|
|
element.style.position = 'absolute'
|
|
element.style.visibility = 'hidden'
|
|
element.style.height = 'auto'
|
|
|
|
const height = getComputedStyle(element).height
|
|
|
|
element.style.width = ''
|
|
element.style.position = ''
|
|
element.style.visibility = ''
|
|
element.style.height = '0px'
|
|
|
|
// Force repaint to make sure the
|
|
// animation is triggered correctly.
|
|
// eslint-disable-next-line no-unused-expressions
|
|
getComputedStyle(element).height
|
|
|
|
// Trigger the animation.
|
|
// We use `requestAnimationFrame` because we need
|
|
// to make sure the browser has finished
|
|
// painting after setting the `height`
|
|
// to `0` in the line above.
|
|
requestAnimationFrame(() => {
|
|
element.style.height = height
|
|
})
|
|
}
|
|
|
|
const onAfterEnter = element => {
|
|
element.style.height = 'auto'
|
|
}
|
|
|
|
const onLeave = element => {
|
|
const height = getComputedStyle(element).height
|
|
|
|
element.style.height = height
|
|
|
|
// Force repaint to make sure the
|
|
// animation is triggered correctly.
|
|
// eslint-disable-next-line no-unused-expressions
|
|
getComputedStyle(element).height
|
|
requestAnimationFrame(() => {
|
|
element.style.height = '0px'
|
|
})
|
|
}
|
|
|
|
return () => h(h(Transition), {
|
|
name: 'expand',
|
|
onEnter,
|
|
onAfterEnter,
|
|
onLeave,
|
|
}, () => slots.default?.())
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<style>
|
|
.expand-enter-active,
|
|
.expand-leave-active {
|
|
overflow: hidden;
|
|
transition: block-size var(--expand-transition-duration, 0.25s) ease;
|
|
}
|
|
|
|
.expand-enter-from,
|
|
.expand-leave-to {
|
|
block-size: 0;
|
|
}
|
|
</style>
|
|
|
|
<style scoped>
|
|
* {
|
|
backface-visibility: hidden;
|
|
perspective: 1000px;
|
|
transform: translateZ(0);
|
|
will-change: block-size;
|
|
}
|
|
</style>
|