Vue3---动画专题

89 阅读8分钟

#完整原文地址见简书www.jianshu.com/p/f3b52299a… #更多完整Vue笔记目录敬请见《前端 Web 笔记 汇总目录(Updating)》



#本文内容提要

  • Vue常规动画写法

  • Vue过渡动画 常规写法与 例程

  • 上例的另一种实现方式

  • 入场动画

  • 出场动画

  • 出入场动画同时实现时,可以简化以上代码

  • 出场入场帧动画

  • 使用name=对动画进行 片段式地 命名

  • 对动画类的完全命名

  • 完全命名的方式 使得 容易接入 第三方库

  • 注意以上案例,将v-if换成v-show也是可以正常运行的

  • 颜色过渡和位移 动画 结合

  • 控制组合动画时长 以某一类型动画的时间为准

  • 控制组合动画时长 以自定义的时长为准

  • 分别 统一 出入场动画的时延

  • 禁用CSS动画,使用JS动画

  • 最基本的 多个 单元素标签 切换案例

  • 带上入场 退场 动画的 两个单元素标签切换

  • 带上入场 退场 动画的 两个组件 的切换

  • <component>组件占位 + is属性 + 双向绑定特性 实现上例效果

  • 列表动画

  • 状态动画


####Vue常规动画写法 >**关键帧 --- CSS类 --- data数据 --- DOM的Class属性**

-- 使用@keyframes [关键帧实例名]配置好关键帧; -- 使用animation配置关键帧以及动画过程到完成时延, 完成动画的定义【写在一个CSS类中(如下的myAnimation)】; -- 在data中定义一个以 上面定义的动画CSS类实例(如myAnimation) 为属性值的 数据字段(如myAnimateData); -- 在dom中使用:class=[以 动画CSS类实例 为属性的 数据字段], 引用这个数据字段myAnimateData)即可,至此完成动画定义; -- 数据字段(如myAnimateData)中,可以通过对 属性值即动画CSS类实例的 布尔值的 改变, 去控制动画的开关,如下 配置false 为关:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <style>
        @keyframes leftToRight {
            0% {
                transform: translateX(-100px);
            }
            50% {
                transform: translateX(-50px);
            }
            0% {
                transform: translateX(0px);
            }
        }
        .myAnimation {
            animation: leftToRight 3s;
        }
    </style>
    <script src="https://unpkg.com/vue@next"></script>
</head>

<body>
    <div id="heheApp"></div>
</body>
<script>
    const app = Vue.createApp({
        data() {
            return {
                myAnimateData: {
                    myAnimation: false
                }
            }
        },
        template: `
            <div>
                <div :class="myAnimateData">heheheda</div>
            </div>`        
    });

    const vm = app.mount('#heheApp');
</script>
</html>

运行效果,没有动画,DOM中也没显示对应的class: 改为true:

<script>
    const app = Vue.createApp({
        data() {
            return {
                myAnimateData: {
                    myAnimation: true
                }
            }
        },
        template: `
            <div>
                <div :class="myAnimateData">heheheda</div>
            </div>`        
    });

    const vm = app.mount('#heheApp');
</script>

运行时产生动画:

可以配合按钮和点击事件,动态 交互地 开关动画:

<script>
    const app = Vue.createApp({
        data() {
            return {
                myAnimateData: {
                    myAnimation: true
                }
            }
        },
        methods: {
          handleClick() {
            this.myAnimateData.myAnimation = !this.myAnimateData.myAnimation;
          }
        },
        template: `
            <div>
                <div :class="myAnimateData">heheheda</div>
                <button @click="handleClick">切换</button>
            </div>`
    });

    const vm = app.mount('#heheApp');
</script>

####Vue过渡动画 常规写法与 例程 **-- 使用`transition: [时延] background-color ease;`定义 在一个时延内的`过渡动画`, ` background-color`指定过渡对象是`背景颜色`,`ease`指定为`平缓地进行`; -- 类似上例,在组件的data中,定义一个 对应CSS类的 `Object类型数据字段`,并在其中包含`过渡动画`和`定义好的背景颜色`的数据字段; -- 在`template`中,使用`:class=[类实例名]`引用`data`中的`CSS类实例`即可; -- 可以准备一个`触发事件`,在事件中`反转`两个背景颜色值,由此可实现过渡动画:** ``` Hello World! heheheheheheda .myTransition { transition: 3s background-color ease; } .aquamarine { background-color: aquamarine; } .orange { background-color: orange; }
``` **运行效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ffc7228843e46158a98429a9f39c903~tplv-k3u1fbpfcp-zoom-1.image)点击按钮 触发事件后 过渡的中途:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2b9fe0ec3b944a46b4849c9bbf64a4c6~tplv-k3u1fbpfcp-zoom-1.image)过渡完毕:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d0eba1c89aea434dbf5c906c8669c749~tplv-k3u1fbpfcp-zoom-1.image)**
####上例的另一种实现方式 **-- 定义`css动画类`, 在dom节点直接使用`class=[CSS动画类]`配置上这个CSS动画类; -- `data`中定义`初始背景颜色键值`,打包成`object`类型, 配置到DOM节点的`:style=`上,作`双向绑定`; -- 把`css`中定义的`背景颜色字段`都去掉, 直接写在`触发事件`的方法中,在其中通过更改`绑定的style字段的值`改变背景颜色;** ``` Hello World! heheheheheheda .myTransition { transition: 3s background-color ease; }
``` **运行效果如上例;**
####入场动画 **如下, ``标签定义的三个类, 分别指定 `入场动画`的`开始时`的透明度、 过程持续的`时延 `和 `速度`(ease -- 缓慢)、 入场动画的`结束时`的透明度; `template`中, 使用`<transition>`标签对 包裹 要实现`入场动画`的DOM节点即可;** ``` <head> ... <style> .v-enter-from{ opacity: 0; } .v-enter-active { transition: opacity 3s ease; } .v-enter-to { opacity: 1; }
``` **运行效果,实现入场动画: 初始: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fdfaf5dee2a04c72841e0b7e5bc3b1c9~tplv-k3u1fbpfcp-zoom-1.image)入场中:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/66312caebdb64da3a95f7cd379593c25~tplv-k3u1fbpfcp-zoom-1.image)入场完毕:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ad8f86395c324ef8a54026925e68c3e7~tplv-k3u1fbpfcp-zoom-1.image)**
####出场动画 **理同入场动画,其中, `v-leave-from`表示 `出场动画`的`开始时`的透明度、 `v-leave-active`表示过程持续的`时延 `和 `速度`(ease -- 缓慢)、 `v-leave-to`表示出场动画的`结束时`的透明度; 其余步骤 同入场动画:** ``` Hello World! heheheheheheda .v-enter-from{ opacity: 0; } .v-enter-active { transition: opacity 3s ease-out; } .v-enter-to { opacity: 1; } .v-leave-from { opacity: 1; } .v-leave-active { transition: opacity 3s ease-in; } .v-leave-to { opacity: 0; } </style> <script src="https://unpkg.com/vue@next"></script>
``` **运行效果: 初始: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2d34e2e58644402f991f941d321f9b69~tplv-k3u1fbpfcp-zoom-1.image)切换中:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/301abba8e0494b5a94f3cb589db70bd3~tplv-k3u1fbpfcp-zoom-1.image)切换结束:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/17d189f883434bdab07dc0cba5beaebb~tplv-k3u1fbpfcp-zoom-1.image)**
####出入场动画同时实现时,可以简化以上代码 **两个`v-xxx-active`可以写到一起, 如果`v-leave-from`指定的值与`v-enter-to`相同(一般都是相同的),那也可以省略它; 以下代码实现的效果,同上:** ``` ... .v-enter-from{ opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 3s ease-out; } .v-enter-to { opacity: 1; } .v-leave-to { opacity: 0; } ... ```
####出场入场帧动画 **准备好一个`keyframes`,然后把它写在`v-xxx-active`中即可:** ``` ··· @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .v-enter-active { animation: shake 2s; } .v-leave-active { animation: shake 2s; } ··· ```
####使用`name=`对动画进行 片段式地 命名 **可以在`template`中, 在``标签中,用`name=`指定一个命名, 然后在style中定义css类时, 将之前的`v-xxx-xxx`等定义符换成`[命名]-xxx-xxx`即可:** ``` Hello World! heheheheheheda @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .heheda-enter-active { animation: shake 2s; } .heheda-leave-active { animation: shake 2s; }
```
####对动画类的完全命名 **即直接在`template`中,在``标签中, 使用`[原css命名] = [新命名]`的方式,对整个CSS类取别名, 用的时候,直接使用新命名即可:** ``` Hello World! heheheheheheda @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .hihi { animation: shake 2s; } .byebye { animation: shake 2s; }
``` **关键代码:** ``` ... ... .hihi { animation: shake 2s; } .byebye { animation: shake 2s; }

...

**运行效果同上例;**


<br>
####完全命名的方式 使得 容易接入 第三方库
**这里以引入`Animate.css`为例;
官网:https://animate.style/ 
官网首页如下,右侧列表是各种动画类型,点击可以预览效果:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/04afd39564964dc3ab6314b369a1164e~tplv-k3u1fbpfcp-zoom-1.image)
官网简略引导:
(如下可知,即`框架`已经把`动画`定义`实现`好了,
使用时只用直接使用`定义好的框架``样式命名`即可)![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/033c6002f8b14045b76be299b3468a9a~tplv-k3u1fbpfcp-zoom-1.image)**
**弹跳效果:**
Hello World! heheheheheheda
``` 闪烁效果: ``` template: `
heheda
切换
` ```
####注意以上案例,将`v-if`换成`v-show`也是可以正常运行的 **区别在于隐藏组件的底层机制不同而已, 在博客笔记[《Vue3 | 条件渲染 与 列表循环渲染》](https://www.jianshu.com/p/695bf35fa466)我们讨论过这个事情;**
####颜色过渡和位移 动画 结合 ``` Hello World! heheheheheheda @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .v-enter-from { color: red; } .v-enter-active { animation: shake 2s; transition: all 2s ease-in; } .v-leave-active { color: red; animation: shake 2s; transition: all 2s ease-in; }
``` **关键代码: 入场,从红色开始,到黑色(默认颜色); 出场,从黑色(默认颜色)开始,到红色; `all`表示`过渡特性`适配到 所用类型的动画;** ``` .v-enter-from { color: red; } .v-enter-active { animation: shake 2s; transition: all 2s ease-in; } .v-leave-active { color: red; animation: shake 2s; transition: all 2s ease-in; } ```
####控制组合动画时长 以某一类型动画的时间为准 **在``标签上, 写上`type="transition"`,则组合动画的时长 以过渡动画 为准; 写上`type="animation`,则组合动画的时长 以普通类型动画 为准;** 如下案例,写上`type="transition"`, 则组合动画在 过渡动画执行完毕(即3s)之后, 会立即停止 正在运行的 所有动画: ``` Hello World! heheheheheheda @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } .v-enter-from { color: red; } .v-enter-active { animation: shake 6s; transition: all 3s ease-in; } .v-leave-active { color: red; animation: shake 6s; transition: all 3s ease-in; }
```
####控制组合动画时长 以自定义的时长为准 **在``标签上, 写上`:duration="[动画时长 / ms]"`,则组合动画的时长 以这里定义的数据 为准, 无论CSS类中,定义了多长的时延:** ``` template: `
heheda
切换
` ```
####分别 统一 出入场动画的时延 **`:duration="[{enter: [入场动画时延], leave:[出场动画时延]}]"`** ``` template: `
heheda
切换
` ```
####禁用CSS动画,使用JS动画 **-- 使用`:css="false"`禁用CSS动画,使得CSS动画都失效(如果有定义的话); -- `@before-enter = "[方法名]"` 定义 `入场就绪` 及 `开始前` 状态的动画回调方法; -- `@enter = "[方法名]"` 定义 `动画运行时` 状态的 回调方法; -- `@after-enter="[方法名]"`定义 `动画结束时` 状态的 回调方法; -- `done();` 通知架构动画已经结束,可以回调 `handleEnterEnd(el)`了; 【注意,以上是`入场动画`的钩子回调, 把`enter`换成`leave`,即是对应的`退场动画`的钩子回调】**

如下代码, handleBeforeEnter(el)指定 初始动画颜色 为 橙色; handleEnterActive(el, done)使得 动画在 橙色 与 蓝色之间 动态切换, 配合 setInterval(() => {}, 1000)时隔一秒切换一次; clearInterval(animation);配合 setTimeout(() => {}, 5000)使得5秒后关闭动画; 随后调用done(); 通知架构 动画已经结束,可以回调 handleEnterEnd(el)了; handleEnterEnd(el)调试性地 弹出一个弹窗;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>

<body>
    <div id="heheApp"></div>
</body>
<script>
    const app = Vue.createApp({
        data() {
            return {
                show: false
            }
        },
        methods: {
          handleClick() {
            this.show = !this.show;
          },  
          handleBeforeEnter(el) {
            el.style.color = "orange";
          },
          handleEnterActive(el, done) {
            const animation = setInterval(() => {
                const color = el.style.color;
                if(color === "orange") {
                    el.style.color = 'blue';
                } else {
                    el.style.color = 'orange';
                }
            }, 1000)
            setTimeout(() => {
                clearInterval(animation);
                done();
            }, 5000)
          },
          handleEnterEnd(el) {
              alert("动画结束!!!");
          }
        },
        template: `
            <div>
                <transition 
                :css="false"
                @before-enter = "handleBeforeEnter"
                @enter="handleEnterActive"
                @after-enter="handleEnterEnd">
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

    const vm = app.mount('#heheApp');
</script>
</html>

运行效果:


####最基本的 多个 单元素标签 切换案例 ``` Hello World! heheheheheheda
```
####带上入场 退场 动画的 两个单元素标签切换 **-- css块上面已经讲过了,这里主要是看`mode="in-out"`; 这里指定为`mode="in-out"`的话, 则切换时 先进行入场节点的 入场 及其 动画【in】, 再进行退场节点 的 入场 及其 动画【out】; 【缺点:入场 与 退场的节点,在入场动画完毕前,会同框】
-- 如 指定为`mode="out-in"`,则顺序与上相反; 【特性:入场 与 退场的节点 不会同框】
-- `appear`特性:初始加载节点的时候,就会启动`入场动画`, 没有加这个标签,`入场动画`需要触发才会启动;** ``` Hello World! heheheheheheda .v-leave-to { opacity: 0; } .v-enter-from { opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 2s ease-in; } .v-leave-from, .v-enter-to { opacity: 1; }
```
####带上入场 退场 动画的 两个组件 的切换 **基于上例进行小改动即可,其原理是类似的, 以下代码实现的效果 同上例;** ``` Hello World! heheheheheheda .v-leave-to { opacity: 0; } .v-enter-from { opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 2s ease-in; } .v-leave-from, .v-enter-to { opacity: 1; }
```
####``组件占位 + is属性 + 双向绑定特性 实现上例效果 **-- `data`字段 存储要展示的子组件 的 组件名; -- 点击事件中 更改`data`字段 以 更换 展示的子组件, 同时产生`组件的 入场退场`,触发相关动画; -- `template`中 使用 ``标签占位, 使用`is`控制要展示的组件, 指定值为 `data`字段;**
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <style>
        .v-leave-to {
            opacity: 0;
        }
        .v-enter-from {
            opacity: 0;
        }
        .v-enter-active,
        .v-leave-active {
            transition: opacity 2s ease-in;
        }
        .v-leave-from,
        .v-enter-to {
            opacity: 1;
        }
    </style>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const ComponentA = {
        template: `<div>heheda</div>`
    }

    const ComponentB = {
        template: `<div>lueluelue</div>`
    }

    const app = Vue.createApp({
        data() {
            return {
                component : 'component-a'
            }
        },
        methods: {
          handleClick() {
            if (this.component === 'component-a') {
                this.component = 'component-b';
            } else {
                this.component = 'component-a';
            }
          },
        },
        components: {
            'component-a': ComponentA,
            'component-b': ComponentB,
        },
        template: `
            <div>
                <transition mode="out-in" appear>
                    <component :is="component">
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

    const vm = app.mount('#heheApp');
</script>
</html>

运行效果同上例;


####列表动画 >**即列表增删元素时,产生的`入场 和 出场动画`;**

主要是对itemViewv-for等标签套上<transition-group>这个特殊动画标签, 然后点击事件时使用unshift在队头添加元素, 其他的定义跟普通动画差不多;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <style>
        .v-enter-from {
            opacity: 0;
            transform: translateY(30px);
        }
        .v-enter-active {
            transition: all .5s ease-in;
        }
        .v-enter-to {
            opacity: 1;
            transform: translateY(0);
        }
        .v-move {
            transform: all .5s ease-in;
        }
        .list-item {
            display: inline-block;
            margin-right: 10px;
        }
    </style>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>
    const app = Vue.createApp({
        data() {
            return {
                list : [1, 2, 3]
            }
        },
        methods: {
          handleClick() {
            this.list.unshift(this.list.length + 1);
          },
        },
        template: `
            <div>
                <transition-group>
                    <span 
                        class="list-item" 
                        v-for="item in list"
                        :key = "item">{{item}}</span>
                </transition-group>
                <button @click="handleClick">增加</button>
            </div>`
    });

    const vm = app.mount('#heheApp');
</script>
</html>

运行效果:


####状态动画 >**显示一个值【状态】 到 另外一个值【状态】 的变化过程, 类似于Android的`ValueAnimator`;** ``` Hello World! heheheheheheda
``` **运行效果:**![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/17a5aea0c7ed4047af5826228ab3de2d~tplv-k3u1fbpfcp-zoom-1.image)
####