Vue transition 页面过渡动画能否只退出/只进入?

function
  • 71

问题:

我在使用 vue2 的 transition 给页面增加过渡动画,遇到一个问题.
能否在动画上,进入时只显示进入动画,退出时只显示退出动画.
否则两个动画都一起显示,会有重叠或者动画混乱的情况.

举例说明场景:

进入动作

a.page ==> b.page

这里实际上是从a页面点击链接进入b页面,在逻辑上是前进动作.
实际在vue代码层面,既是b页面的进入,也是a页面的退出.

期望: 只显示b页面进入

退出动作/back动作

b.page ==> a.page

同理当前操作,既是b页面的退出,也是a页面的进入.

期望: 只显示b页面退出

测试代码1

能够清晰看到重叠的页面

<template>
<transition name="animation">
    <router-view></router-view>
</transition>
</template>
<style>

.animation-enter-active{
        animation: aaa 1.5s;
    }
    .animation-leave-active{
        animation: aaa 1.5s reverse;
    }
    @keyframes aaa{
        0% {
            opacity: 0;
            transform: translateX(100%);
        }
        100% {
            opacity: 1;
            transform: translateX(0%);
        }
}
</style>

测试代码2

<template>
<transition enter-active-class="slideIn" leave-active-class="slideOut" >
    <router-view></router-view>
</transition>
</template>
<style>
.slideIn{
    animation: a .2s forwards
}
.slideOut{
    animation: b .2s forwards
}
</style>
回复
阅读 124
1 个回答

经过一天一夜的研究,各位大佬的帮助。
首先说有两中解决方案:

1. 仿APP页面过渡操作

简单说明:任何页面跳转动作,都包含新页面进入,旧页面退出动作。
前进:

a.page ==> b.page
b页面进入动画,a页面退出动画。

看起来很舒滑,基本纯 CSS 实现。但不是我期望的,也是个不错的方案。
思路1 https://www.jianshu.com/p/386...
思路2 https://github.com/YellowDoin...

2.弹出层级,也是仿APP操作

简单说明:根据页面跳转动作,分别判断是否显示进入动画,退出动画。

前进:

a.page ==> b.page
b页面在上层进入动画,a页面在下层原地不动n秒后消失。

后退:

a.page <== b.page
a页面直接显示在底层,b页面在上层退出遮罩动画直至消失。

实现方法:

<template>
<transition v-on:enter="enter" v-on:leave="leave" v-bind:css="false">
    <router-view ></router-view> -->
</transition>
</template>

<style>
/* 分别定义进场、退场动画 */
.fade-in{
    animation: a 0.2s forwards;
}
.fade-out{
    animation: b 0.2s forwards
}
</style>

<script>
export default {
  name: 'App',
    data(){
        return {
            pageAction:'',
            pageActionSleep:200,
        };
    },
    methods: {
        enter: function (el, done) {
            if(this.pageAction=='slide-in'){
                $(el).css('z-index',999999)
                $(el).addClass('fade-in').on('animationend webkitAnimationEnd',function(){
                    done()
                    $(el).css('z-index',1)
                })
            }else if(this.pageAction=='slide-out'){
                $(el).css('z-index',1)
                setTimeout(() => {
                    done()
                }, this.pageActionSleep);
            }else{
                return done();
            }
            console.log('enter pageAction:'+this.pageAction)
        },
        leave: function (el, done) {
            if(this.pageAction=='slide-out'){
                $(el).css('z-index',999999)
                $(el).addClass('fade-out').on('animationend webkitAnimationEnd',function(){
                    done()
                    $(el).css('z-index',1)
                })
            }else if(this.pageAction=='slide-in'){
                $(el).css('z-index',1)
                setTimeout(() => {
                    done()
                }, this.pageActionSleep);
            }else{
                return done();
            }
            console.log('leave pageAction:'+this.pageAction)
        }
        
    },
    watch: {
        '$route' (to, from) {
            const toDepth = to.path.split('/').length
            const fromDepth = from.path.split('/').length
            if(toDepth == fromDepth){
                    // 同级无动画
                this.pageAction =  '';
            }else{
                    // 判断页面是前进还是后退。
                this.pageAction = toDepth < fromDepth ? 'slide-out' : 'slide-in'
            }
            console.log(toDepth < fromDepth ? 'fade-out' : 'fade-in')
        }
    },
    
}
</script>

补充

在写完上面答案后,我又想到了,可以更少量的通过js辅助执行。
只需要在路由变动监听事件里针对前进后退分别进行css绑定。

vue 有两个事件:

enter-active-class 进入动画CSS绑定
leave-active-class 离开动画CSS绑定

我们可以将这两个绑定改成动态的,通过变量影响其动画类型。

<transition :enter-active-class="fadein" :leave-active-class="fadeout">
    <router-view ></router-view>
</transition>

其中,fadein 为元素进场时的动画。fadeout为元素退场时的动画。

试想一下,一个页面的back操作。

a.page <== b.page

是b页面的退出,也是a页面的进入。
我们通过css让b页面退出时置顶显示,让a页面垫底,做原地不动动画。这样当b页面退场后,看起来a页面就没有动作,一直在底部等着我们。

定义一个css类名,比如

.page-back-old 页面退场时的旧页面动画
.page-back-new 页面退场时的新页面动画

同样,前进时也是如此:

.page-enter-old 页面入场时的旧页面动画
.page-enter-new 页面入场时的新页面动画

完善一下CSS:

.page-back-old{
    z-index: 10  !important;
    animation: b 0.2s forwards;
}
.page-back-new{
    z-index: 1  !important;
    animation: animation-none 0.2s;
}
.page-enter-old{
    z-index: 1;
    animation: animation-none 0.2s;
}
.page-enter-new{
    z-index: 10;
    animation: a 0.2s forwards;
}

@keyframes animation-none{
        0% {
        }
        100% {
        }
}

现在,我们分别写了两种场景下的,元素进入和退出动画。那么只需要在对应的情况下将对应的类名绑定到 transition 里就可以实现了。

再说一下事件产生:

如果你在 transition 里将动画写死,固定一个入场一个出场动画,无论页面还是退出还是进入,那么就是会有我最早提出的问题,两个动画会进行冲突对撞或者其他重叠问题。

这里根据页面动作分别展示不同的动画绑定,如果有人和我之前一样有这个疑问。
当页面交换时,两个页面元素都会同时同级展示在网页中,如果将进入和退场动画改了,那岂不是两个页面的进入退场都一起改了。

答案是确实会一起改,但是不影响,虽然a、b页面在当下退出和进入事件都一样,但他们同时执行时只会执行其中一个事件,进入或退出。
或许是a页面的进入,b页面的退出。又或许时b页面的进入,a页面的退出。
所绑定的事件不会全部执行,我们没法区分哪个div是新页面还是旧页面,只能通通改掉。但改掉也并不影响,因为不会去执行。

最后补上路由监听代码:

    watch: {
        '$route' (to, from) {
            const toDepth = to.path.split('/').length
            const fromDepth = from.path.split('/').length
            if(toDepth == fromDepth){
                this.pageAction =  '';
            }else{
                this.pageAction = toDepth < fromDepth ? 'slide-out' : 'slide-in'
            }
            if(this.pageAction == 'slide-out'){
                this.fadein = "page-back-new";
                this.fadeout = "page-back-old";
            }else if(this.pageAction == 'slide-in'){
                this.fadein = "page-enter-new";
                this.fadeout = "page-enter-old";
            }else{
                this.fadein = this.fadeout = ""
            }
            console.log(toDepth < fromDepth ? 'fade-out' : 'fade-in')
        }
    },
你知道吗?

宣传栏