You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
3.2 KiB
Vue

<template>
<transition
:name="transitionName"
:mode="transitionMode"
:enter-active-class="transitionEnterActiveClass"
@beforeLeave="beforeLeave"
@enter="enter"
@afterEnter="afterEnter"
>
<slot />
</transition>
</template>
<script>
const DEFAULT_TRANSITION = 'fade'
const DEFAULT_TRANSITION_MODE = 'out-in'
export default {
name: 'TransitionPage',
data () {
return {
prevHeight: 0,
transitionName: DEFAULT_TRANSITION,
transitionMode: DEFAULT_TRANSITION_MODE,
transitionEnterActiveClass: '',
}
},
created () {
this.$router.beforeEach((to, from, next) => {
let transitionName =
to.meta.transitionName ||
from.meta.transitionName ||
DEFAULT_TRANSITION
if (transitionName === 'slide') {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
this.transitionMode = DEFAULT_TRANSITION_MODE
this.transitionEnterActiveClass = `${transitionName}-enter-active`
if (to.meta.transitionName === 'zoom') {
this.transitionMode = 'in-out'
this.transitionEnterActiveClass = 'zoom-enter-active'
document.body.style.overflow = 'hidden'
}
if (from.meta.transitionName === 'zoom') {
this.transitionMode = null
this.transitionEnterActiveClass = null
document.body.style.overflow = null
}
this.transitionName = transitionName
next()
})
},
methods: {
beforeLeave (element) {
this.prevHeight = getComputedStyle(element).height
},
enter (element) {
const { height } = getComputedStyle(element)
// eslint-disable-next-line no-param-reassign
element.style.height = this.prevHeight
setTimeout(() => {
// eslint-disable-next-line no-param-reassign
element.style.height = height
})
},
afterEnter (element) {
// eslint-disable-next-line no-param-reassign
element.style.height = 'auto'
},
},
}
</script>
<style lang="scss">
.fade-enter-active,
.fade-leave-active {
transition-duration: 0.3s;
transition-property: height, opacity;
transition-timing-function: ease;
overflow: hidden;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition-duration: 0.5s;
transition-property: height, opacity, transform;
transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1);
overflow: hidden;
}
.slide-left-enter,
.slide-right-leave-active {
opacity: 0;
transform: translate(2em, 0);
}
.slide-left-leave-active,
.slide-right-enter {
opacity: 0;
transform: translate(-2em, 0);
}
.zoom-enter-active,
.zoom-leave-active {
animation-duration: 0.5s;
animation-fill-mode: both;
animation-name: zoom;
}
.zoom-leave-active {
animation-direction: reverse;
}
@keyframes zoom {
from {
opacity: 0;
transform: scale3d(0.3, 0.3, 0.3);
}
100% {
opacity: 1;
}
}
</style>