55

Vue 2.0学习笔记:Vue的transition

 5 years ago
source link: https://www.w3cplus.com/vue/vue-transition.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

动效在Web中一直是一个有争议的问题。动效做得好有助于在你的Web程序上锦上添花,甚至是留住你的用户,也可以具有较好的用户体验;反之,如果动效运用的不好,会给用户带来一种反感,让用户迅速地离开你的应用。怎么提供更友好的动效,并不是今天我们要讨论的重点,我们要讨论的是: 在Vue应用程序中如何添加动效? 在Vue中,提供了多种方法来给你的运用程序添加动效,比如CSS的 transitionanimation 动效,以及在Vue的生命周期的钩子函数中操作DOM。甚至你还要以使用第三方动画库,比如 GSAPVelocity.js 来制作动效。

在本文中,我们将先重点了解Vue中处理CSS的 transition 的原理。有了这些知识,就可以开始创建自己的过渡动效。一旦掌握了这些基础知识,就可以快速掌握Vue中的 transitionanimation 的全部功能。

transition vs animation

在具体了解Vue中的 transition 之前,咱们先简单的了来了解一下 transitionanimation 之间的差异(这里所指的是CSS中两者的差异)。先上一张录制的动图:

yqu2qyQ.gif

两者的效果就如下图所示这样:

VNBJjm2.png!web

如果用代码来描述的话:

/* transition*/
.button {
    background: blue;
    transition: background;

    &:hover {
        background: red;
    }
}

/* animation */
@keyframe spin {
    0% {
        color: red;
    }
    50% {
        color: blue;
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

.element {
    animation: spin 1s ease-in-out 2s infinite;
}

也就是说, transition 是指一个元素从 状态 A 状态B ,即开始状态是 A ,结束状态是 B 。而其主要的目的是创建一个自然的演示,告诉用户的交互行为已经发生了更改(就是从 A 变成了 B )。在现实的Web应用中这样的示例也非常的多。比如下拉菜单的效果就是其中的一个典型案例。默认情况下,下拉菜单是收缩闭合的(隐藏不见,就是我们所说的状态 A ),当用户点击了或者鼠标悬浮在菜单项上,下拉菜单就会展开(下拉菜单可见,就是我们所说的状态 B )。这样的一个过程如果添加了 transition 的话,就自钱的完成了闭合(隐藏)自动过渡到展开(可见)状态。比如下图所示:

JNnIn2q.gif

animationtransition 有很大的不同之处,从状态 A 到状态 B 之间有很多中间态,而这些被称为 关键帧 。因此, animation 也常常被称为 帧动画 ,也有人称为 补间动画 。比如下图所示:

nEZNRrJ.png!web

从上图我们也可以看出来, animation 有很多中间态,即,可以从 AB ,再到 C ,再到 D ,甚至更多。其主要目的是 不断展示某些东西正在改变 。它们可以有结束状态,但与 transition 不同,它们不限于两种状态。比如下面这样的一个示例,它不断地从一种状态变化到另一种状态,但它最终可能会结束。

QRv2uya.gif

在某种程度上, animation 只是 transition 的一个超级集合,因为它添加了更多的中间状态。虽然 transition 只是从状态 A 到状态 B,但 animation 可以根据需要拥有任意多的中间状态。也就是说,只要理解了 transition 的基本原理之后,在去理解 animation 就不是什么一件难事了。这也就是我们今天为什么把重点放在 transition`上。

transition元素

在Vue中有一个 <transition> 元素(即一个容器),它主要用来处理元素或组件上的 transition 动效,CSS和JavaScript的 animation 动效,而且会让你处理这些动效变得简地多。而在CSS的 transition 动效中, <transition> 元素主要负责应用和取消类(元素的类名)。而你所要做的就是定义元素在 transition 动效期间元素的样式。

<transition> 在Vue中使用非常的简单,把要带动效的元素放到这个容器之中,同时使用 name 给其一个名称,比如 fade

<transition name="fade">
    <h1 v-if="show">Hello! W3cplus.com (^_^)</h1>
</transtion>

当在 <transition> 容器中的元素在显示(插入)或隐藏(删除)时,Vue会自动嗅探到目标元素是否应用了CSS的 transitionanimation ,如果是,在恰当的时机添加或删除CSS类名。

简单地说:

<transition> 是Vue已经封装好的一个组件,可以给任何元素和组件添加进入或离开过渡效果。

在Vue中的下面这几种情形会产生相应的过渡或动画效果:

v-if
v-show

比如下面这个示例:

<!-- FadeText -->
<template>
    <div class="wrapper">
        <transition name="fade">
            <h1 v-if="isShow">{{ msg }}</h1>
        </transition>
        <button @click="isToggle" :class="isShow ? 'is-show' : 'is-hidden'">
            {{ isShow ? "隐藏" : "显示" }}
        </button>
    </div>
</template>

<script>
export default {
    name: "FadeText",
    data() {
        return {
            isShow: true,
            msg: "Hello! W3cplus.com (^_^)"
        };
    },
    methods: {
        isToggle() {
            this.isShow = !this.isShow;
        }
    }
};
</script>

<style scoped>
    .fade-enter-active,
    .fade-leave-active {
        transition: opacity 0.5s;
    }
    .fade-enter,
    .fade-leave-to {
        opacity: 0;
    }
</style>

效果如下:

上面这个简单的过渡效果使用的是CSS的 transition 方式来实现。那么 v-if 在切换元素 h1 的时候发生了什么呢?

  • v-if 绑定的值 isShowtruefalse 之间切换的时候, <transition> 中的元素 <h1> 会插入和删除之间进行切换
  • 自动嗅探目标元素 h1 是否应用了CSS的 transitionanimation 。如果是,则会在元素插入时添加CSS类名,并判断动画加载完之后删除CSS类名
  • 如果过渡组件提供了 JavaScript钩子函数 (这部分后面会介绍),这些钩子函数在恰当的时机被调用。在这个示例中并没有用到JavaScript钩子函数,所以不会被执行
  • 如果没有找到JavaScript钩子并且也没有检测到CSS的 transitionanimation ,DOM操作(插入或删除)在下帧中立即执行。( 注意:此指浏览器逐帧动画机制,和 Vue 的 nextTick 概念不同 )

这个时候,通过浏览器的动画( Animations )选项可以看到动效的整个过程,如下图所示:

rieQBzi.gif

过渡的类名

在Vue中使用 <transition> 制作过渡效果时,可以给过渡元素添加类名,在Vue中有六个类名,可以使用它们分别处理处理元素插入和删除时过渡效果。其中三个类用于处理元素插入时状态 A 到状态 B 过渡,另外三个类用于处理元素删除时状态 A 到状态 B 的过渡。

在插入或显示组件时发生 enter 过渡,对应的类名是 v-enterv-enter-activev-enter-to ;在隐藏或删除组件时发生 leave 过渡,对应的类名是 v-leavev-leave-activev-leave-to 。每个类对应的作用如下:

  • v-enter :定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
  • v-enter-active :定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
  • v-enter-to : 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
  • v-leave : 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
  • v-leave-active :定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
  • v-leave-to : 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

用下图来阐述将会更清晰一些:

fmMFfm2.png!web

对于这些在过渡中切换的类名来说,如果你使用一个没有使用 name<transition> ,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="fade"> ,那么 v-enter 会替换为 fade-enter ,其他类名也是如此。另外, v-enter-activev-leave-active 可以控制进入/离开过渡的不同的缓和曲线,有关于这部分将在后面会介绍。

结合起来,我们可以用一张图来表示Vue中过渡动效的生命周期,即动效开始、过程、结束对应的类的变化:

auy2myb.png!web

回到上面的示例中来,在元素 h1 加载到DOM之前, h1 会添加 fade-enter 类名,对应的动效也就被添加到该元素中,只对应一帧。因此,在DOM渲染时,将动画的 opacity:0 应用到 h1 元素上,此时该元素也就隐藏不可见。在动效的整个过程中, fade-enter-active 类也被添加到该元素中,在该示例中,动画被设置为 .5sfade-leavefade-leave-active 也以相同的方式运用于 h1 元素上。

在示例中,并没有显式的在 fade-leave 类中设置 opacity: 1 ,那是因为 opacity 的默认值已经是 1 。这也就是为什么在 fade-enter-active 结束时 opacity 也不显式设置为 1 的原因。

在Vue中,如果元素放置到 <transition> 中,Vue会自动检测元素上的 v-if 指令。比如上面的示例,如果 isShow 的值为 true ,那么 h1 会渲染显示,当你点击按钮时,会自动切换 isShow 的值。从而也控制了 h1 元素的显示或隐藏(插入或删除)。

这里需要注意的一点是, Vue只能对 <transition> 中的一个元素进行动效处理。相反,在任何给定的实例中,只能将 <transition> 中的一个元素插入到DOM中 。比如下同这个示例,在Vue中无法正常的工作:

<!-- ToggleAlert.vue -->
<template>
    <div class="toogle-alert">
        <button @click="isToggle" :class="isShow ? 'is-show' : 'is-hidden'">
        {{ isShow ? "隐藏" : "显示" }}
        </button>

        <transition name="fade">
            <div class="alert alert-info" v-if="isShow" key="info">{{ alertInfoMsg }}</div>
            <div class="alert alert-error" v-if="isShow" key="error">{{ alertErrorMsg }}</div>
        </transition>
    </div>
</template>

<script>
export default {
    name: "ToggleAlert",
    data() {
        return {
            isShow: true,
            alertInfoMsg: "Hello! W3cplus.com!",
            alertErrorMsg: "Goodbye! W3cplus.com!"
        };
    },
    methods: {
        isToggle() {
            this.isShow = !this.isShow;
        }
    }
};
</script>

<style scoped>
.fade-enter,
.fade-leave-to {
    opacity: 0;
}
.fade-enter-active,
.fade-leave-activ {
    transition: opacity 0.5s cubic-bezier(1, 0.5, 0.8, 1);
}
</style>

这个时候浏览器会报错:

IrAR3mE.png!web

如果我们把另一个 divv-ifv-else 来替代:

在Vue中 <transition> 中有多个元素需要有动效效果时,需要使用 <transition-group> 来替代,不然在Vue中则会报错。有关于 <transition-group> 更详细的介绍,我们后续会单独花时间来阐述。另外,在 <transition> 中有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在 <transition> 组件中的多个元素设置 key 是一个更好的实践。

现在,你看到的效果可以没有你想像的那么顺滑。这主要是因为,Vue在将第二个元素插入到DOM之前,并不会等第一个元素从DOM中完全删除。所以你看到效果就如上例所示,两个元素之间动效的切换似乎有点闪跳一样。不过我们可以通过 transition 的过渡模式来处理。

过渡模式

上面的示例存在一个问题,先看下图这个效果:

fqQbUzZ.gif

div.alert-infodiv.alert-error 两个元素都被重绘了,一个离开过渡的时候另一个开始进入过渡。其实这也是 <transition> 的默认行为,即 进入和离开同时发生

在Vue中提供了两种过渡模式来解决上述问题:

  • in-out :新元素先进行过渡,完成之后当前元素过渡离开
  • out-in :当前元素先进行过渡,完成之后新元素过渡进入

这样我们就可以使用 out-in 重写上面的示例:

<transition name="fade" mode="out-in">
    <div class="alert alert-info" v-if="isShow" key="info">
        {{ alertInfoMsg }}
    </div>
    <div class="alert alert-error" v-else="!isShow" key="error">
        {{ alertErrorMsg }}
    </div>
</transition>

效果如下:

设置初始渲染的过渡

在Vue中,可以通过 appear 特性设置节点在初始渲染的过渡。

<transition name="fade" mode="out-in" appear>
    <div class="alert alert-info" v-if="isShow" key="info">
        {{ alertInfoMsg }}
    </div>
    <div class="alert alert-error" v-else="!isShow" key="error">
        {{ alertErrorMsg }}
    </div>
</transition>

这里默认和进入和离开过渡一样,同样也可以自定义 CSS 类名:

<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class" 
appear-active-class="custom-appear-active-class"
>
    <!-- ... -->
</transition>

同样可以对应的类名添加样式:

.custom-appear-class {
    opacity: 0;
    transform: translateX(100%);
}
.custom-appear-active-class {
    transition: 2s;
}

比如上面的示例,按照上面的代码进行调整之后,可以发看到第一个 .alert 插入到DOM中时,整个 .alert 会从右侧移入进来,效果如下:

示例: 使用transition制作一个圆形菜单

通过上面的学习,我们接下来使用Vue的 transition 来实现下面这样的一个圆形菜单效果:

ZNNfQbm.gif

这个案例其实非常的简单,通过一个 <transition> 来触发多个子元素的过渡效果,我们只需要定义元素对应的过渡效果即可,而其他的事情都将交给Vue来搞定。

<transition name="move" mode="out-in" appear>
    <ul class="menu" v-show="isShow">
        <li
        v-for="(item, index) in menus"
        :key="index"
        class="menuitem-wrapper"
        >
            <div class="icon-holder">
                <a href="#" class="menu-item">
                    <i class="material-icons">{{ item }}</i>
                </a>
            </div>
        </li>
    </ul>
</transition>

只需要在对应的 transition 类中控制 transform 的样式:

.move-enter-active,
.move-leave-active {
    transition: all 0.08s ease-in-out;
}

.move-enter,
.move-leave-to {
    transform: scale(0);
}

最终效果如下:

总结

这篇文章主要介绍了Vue中如何使用 <transition> 来实现 transition 动效。这只是Vue中制作动效的最基础部分,但这些基础部分是帮助我们实现更为复杂的动效的基础。在接下来中我们将再一起探讨如何在Vue中实现CSS的 animation 效果,以入如何和第三方库一起结合实现动效。如果感兴趣的话,欢迎持续关注后续的相关更新。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK