Vue3:第十章 Vue3中的过渡和动画效果实现

在前两节中,我们已经完整的介绍了Vue3中的组件化开发以及如何去灵活地使用组件,在掌握了组件相关的开发技能后,相信对于大家来说,实现一个自定义的组件已经不是什么问题了。

虽然在技术上没有什么难点了,但我们实现的组件相比与ElementUI这些成熟的UI框架来说,我们会发现在一些细节上还存在差距,还是以我们的弹框组件为例,ElementUI中的弹框在出现和隐藏的时候,会有个垂直方向的位移和淡入淡出的动画效果。

那么本节我们就来聊一聊Vue3中要怎么实现组件的过渡和动画效果。

前端怎么实现动画效果?

提到前端动画,我们一般都会想到通过css3的transitionanimation属性,如果我们要实现一个鼠标移到矩形上时,矩形宽度增加的效果。

使用transition来实现的话,代码如下。

<style>
    div{
        margin: 100px;
        width: 100px;
        height: 60px;
        background-color: bisque;
    }
    div:hover{
        width: 200px;
        transition: width 1s linear;
    }
</style>

从代码中我们可以看到,transition支持配置动画属性,过渡时长以及运动的速度曲线,这就是一个简单的过渡效果,同样,我们可以使用animation来实现一个循环转圈的动画,实现效果如下。

turn.gif
<style>
    div{
        margin: 100px;
        width: 100px;
        height: 60px;
        background-color: bisque;
        animation: turn 2s linear infinite;
    }

    @keyframes turn {
        0% {
            transform: rotate(0deg);
        }
        25% {
            transform: rotate(90deg);
        }
        50% {
            transform: rotate(180deg);
        }
        75% {
            transform: rotate(270deg);
        }
        100% {
            transform: rotate(360deg);
        }
    }
</style>

如上,通过animation我们可以在网页端实现一些流畅的动画效果。

通过上面的实例,可以发现过渡和动画虽然表现形式类似,但还是存在一些区别的。

  • 过渡一般需要一个触发条件,比如鼠标的点击、滑入,而动画不需要。
  • 过渡只需要关注开始和结束这两个关键帧,而动画可以通过百分比设置过程中各个时刻的效果。

这里插一句,我们在实现动画的过程中,很少会使用top、left之类的属性,而是使用transform来代替,主要是因为top、margin等属性会触发重绘,而transform不会,可以更好的保障页面的流畅,提升性能。

我们已经简单的介绍了前端常用动画的实现,在实际的开发中,也离不开各种动画效果来让我们的页面交互更加友好自然,那么在Vue3中,我们要如何实现过渡和动画效果呢?

transition组件

可能有的同学已经知道甚至使用过Vue中的transition组件,transition主要使用在组件进入和离开的时候,当然我们也可以自己通过css去定义动画。

我们来增加一个按钮,控制div的显示隐藏,看下在div显示和隐藏的过程中transition怎样实现动画效果。

<style scoped>
    div{
        margin-left: 100px;
        width: 100px;
        height: 60px;
        background-color: bisque;
    }
</style>

<template>
    <button @click="showDiv">按钮</button>
    <div v-if="show"></div>
</template>

<script setup>
    import {ref} from 'vue';

    let show = ref(false);

    function showDiv () {
        show.value = !show.value;
    }
</script>

通过上面的代码,我们很简单的就实现了控制div的显示和隐藏,但是这个过程都是瞬间完成的,没有任何的过渡效果。

接下来我们通过transition组件来给这个过程添加动画,将要增加过渡动画的元素放到transition组件内。

<template>
     <button @click="showDiv">按钮</button>
+    <transition name="widen">
        <div v-if="show"></div>
+    </transition>
</template>

transition组件有6个过渡的class(v-指代的就是transition中定义的name属性,默认为v-,我们只需要在对应状态的类中规定好该状态的样式就可以实现过渡效果了。):

  • v-enter-from/v-leave-from: 对应过渡进入/离开的开始状态。
  • v-enter-active/v-leave-active: 对应过渡进入/离开生效时的状态。
  • v-enter-to/v-leave-to: 对应过渡进入/离开的结束状态。

上面代码中div的长度是100像素,我们期望在div显示的时候,长度是从0像素过渡增加到100像素的。

根据上面的结论我们知道,过渡效果我们只需要关注两个关键帧的样式——过渡的开始和结束就可以了,在div显示的过渡动画中,开始帧对应div从什么样式开始,结束帧对应div本身已经定义好的样式width: 100px

我们希望长度从0开始,所以开始帧的样式应该为width: 0px,对应的过渡class为v-enter-from,代码如下:

// 结束帧样式
div{
    margin-left: 100px;
    width: 100px;
    height: 60px;
    background-color: bisque;
}
// 开始帧样式
.widen-enter-from{
    width: 0;
}

除了两个关键帧以外,我们还需要对过渡的过程设定一些效果,比如使用哪个属性进行过渡,过渡的持续时间和曲线效果,对应的class就是v-enter-active:

// 生效状态
.widen-enter-active{
    transition: width 1s linear;
}

可以看到效果如下:

大家可以自己尝试下隐藏div时,让长度从100像素到0像素的动画效果,这个时候div的样式就是开始帧的样式了,width: 0px才是结束帧样式,只需要抓住过渡的两个关键帧就可以很好的实现你要的效果。

既然有transition这个组件可以实现过渡,那么下面就让我们用transition组件来优化下弹框组件的显示动画。

// 给弹框组件的内容包裹transition组件
+ <transition name="modal">
    <div class="modal-content" v-show="modalShow">
        <div class="modal-header">{{props.title}}</div>
        <div class="modal-body">
            <div>{{props.content}}</div>
            <slot></slot>
        </div>
        <ModalFooter @handleClose="onCancel" />
    </div>
+ </transition>

首先,我们给弹框组件添加一个垂直方向上的移动动画,修改弹框垂直方向的位置最容易想到的就是修改弹框的top值,而在上面我们已经说明过top之类的属性改动会影响页面性能,所以我们使用transform来实现移动。

<style scoped>
    .modal-content{
        ...
+       transform: translateY(10px);
    }
+   .modal-enter-from{
+       transform: translateY(0);
+   }
+   .modal-enter-active{
+       transition: transform 0.3s linear;
+   }
</style>
translate.gif

这样垂直方向的位移动画就已经实现了,继续来添加一个淡入的效果,其实就是在弹框出现的时候让透明度从0到1,这个效果的实现类似我们上面的div的宽度过渡。

<style scoped>
    .modal-content{
        ...
        transform: translateY(10px);
+       opacity: 1;
    }
    .modal-enter-from{
        transform: translateY(0);
+       opacity: 0;
    }
    .modal-enter-active{
+       transition-property: transform, opacity;
+       transition-duration: 0.5s;
+       transition-timing-function: linear;
    }
</style>

因为同时有多个属性过渡,使用transition-property可以同时设置多个属性,我们将过渡时间调大一点,效果可以看的更明显一点。

在隐藏弹框的时候,只需要将动画效果反过来就可以了(.modal-enter-from与.modal-leave-to的状态其实是一致的,这里需要注意蒙层的效果)。

  .modal-enter-from,
+ .modal-leave-to{
      transform: translateY(0);
      opacity: 0;
  }
  .modal-enter-active,
+ .modal-leave-active{
      transition-property: transform, opacity;
      transition-duration: 0.5s;
      transition-timing-function: linear;
  }
leave.gif

transition-group组件

我们已经了解了如何使用transition组件在Vue3中实现动画效果,但有的时候,我们想对一个列表的内容进行动画渲染,比如通过v-for渲染的li标签,transition组件只能对每次渲染一个节点的情况生效,那么这个时候就需要用到列表过渡<transition-group>

我们定义一个列表,每次点击按钮的时候,随机产生1-10之间的数字,插入到列表的随机位置,用Vue3的Composition Api,我们很轻松就能实现这个功能。

<template>
    <button @click="add">按钮</button>
    <ul>
        <li v-for="item in array" :key="item">{{item}}</li>
    </ul>
</template>

<script setup>
    import {reactive} from 'vue';

    const array = reactive([]);

    function add() {
        // 生成1~10的随机数
        const num = Math.floor(Math.random() * 10 + 1);
        // 生成列表的随机插入位置
        const index = Math.floor(Math.random() * array.length);
        // 在列表中插入随机数
        array.splice(index, 0, num);
    }
</script>

接下来给li列表绑定动画效果,每次新增内容,从左侧往右侧滑动出现。

<template>
     <button @click="add">按钮</button>
+    <transition-group tag="ul" name="list">
        <li v-for="item in array" :key="item">{{item}}</li>
+    </transition-group>
</template>

这里就要用到transition-group组件了,transition-group的tag属性可以指定transition-group渲染成一个页面元素,这里我们将transition-group组件渲染成ul标签,name属性与transition的name属性作用相同。

在css中,我们添加开始和进行中的样式。

.list-enter-from{
    transform: translateX(30px);
}
.list-enter-active{
    transition: transform 0.5s linear;
}

效果如下:

group.gif

我们要的效果基本上已经实现了,但是在列表中插入内容的时候,下方的内容会被挤下去,会显得很生硬,为了解决这个问题,transition-group提供了一个v-move类,会在元素定位被改变时应用。

.list-enter-from{
    transform: translateX(30px);
}
.list-enter-active{
    transition: transform 0.5s linear;
}

+ .list-move{
+     transition: transform 0.5s ease;
+ }

这样就实现了列表的平滑插入了。

组件之间的过渡

上面我们介绍了在Vue3的组件中,如何实现过渡动画效果,那么在组件之间,是否也可以添加过渡动画呢?

答案是可以的,并且与组件中的动画没有什么区别:

<transition name="component-fade">
    <component :is="Component" />
</transition>
...
<style>
.component-fade-enter-from{
  opacity: 0;
}
.component-fade-enter-active{
  transition: opacity 0.5s linear;
}
</style>

transition的使用方法完全一样,只需要将我们过渡的组件包裹到transition组件下进行渲染,最终效果如下。

总结

本节我们介绍了Vue3中如何实现过渡动画,transition组件和列表过渡组件transition-group的使用以及对应的6个状态类,优化了我们的弹框组件,并且进一步将过渡动画应用到组件之间的切换中。

动画在我们的研发过程中是非常实用的技巧,也有很多封装的类库供我们使用,比如animate.css也是可以集成到我们的Vue3项目中的,感兴趣的同学可以自己去尝试一下更多的动画效果。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇