Vue3 | 动画专题

完整原文地址见简书https://www.jianshu.com/p/f3b52299aaa6

更多完整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类实例即可;
-- 可以准备一个触发事件,在事件中反转两个背景颜色值,由此可实现过渡动画:

<!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>
        .myTransition {
          transition: 3s background-color ease;
        }
        .aquamarine {
          background-color: aquamarine;
        }
        .orange {
          background-color: orange;
        }
    </style>
    <script src="https://unpkg.com/vue@next"></script>
</head>

<body>
    <div id="heheApp"></div>
</body>
<script>
    const app = Vue.createApp({
        data() {
            return {
                myTransitData: {
                  myTransition: true,
                  aquamarine: true,
                  orange: false
                }
            }
        },
        methods: {
          handleClick() {
            this.myTransitData.aquamarine = !this.myTransitData.aquamarine;
            this.myTransitData.orange = !this.myTransitData.orange;
          }
        },
        template: `
            <div>
                <div :class="myTransitData">heheheda</div>
                <button @click="handleClick">使用transition切换背景色</button>
            </div>`
    });

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

运行效果:

点击按钮 触发事件后 过渡的中途:
过渡完毕:


上例的另一种实现方式

-- 定义css动画类
在dom节点直接使用class=[CSS动画类]配置上这个CSS动画类;
-- data中定义初始背景颜色键值,打包成object类型,
配置到DOM节点的:style=上,作双向绑定
-- 把css中定义的背景颜色字段都去掉,
直接写在触发事件的方法中,在其中通过更改绑定的style字段的值改变背景颜色;

<!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>
        .myTransition {
          transition: 3s background-color ease;
        }
    </style>
    <script src="https://unpkg.com/vue@next"></script>
</head>

<body>
    <div id="heheApp"></div>
</body>
<script>
    const app = Vue.createApp({
        data() {
            return {
                styleObj: {
                  background: "aquamarine"
                }
            }
        },
        methods: {
          handleClick() {
            if (this.styleObj.background === 'aquamarine') {
                    this.styleObj.background = 'orange';
                } else {
                    this.styleObj.background = 'aquamarine';
                }
            }
        },
        template: `
            <div>
                <div class="myTransition" :style="styleObj" >heheheda</div>
                <button @click="handleClick">使用transition切换背景色</button>
            </div>`
    });

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

运行效果如上例;


入场动画

如下,
<style>标签定义的三个类,
分别指定 入场动画开始时的透明度、
过程持续的时延速度(ease -- 缓慢)、
入场动画的结束时的透明度;
template中,
使用<transition>标签对 包裹 要实现入场动画的DOM节点即可;

<head>
    ...
    <style>
        .v-enter-from{
            opacity: 0;
        }
        .v-enter-active {
            transition: opacity 3s ease;
        }
        .v-enter-to {
            opacity: 1;
        }
    </style>
    <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;
          }  
        },
        template: `
            <div>
                <transition>
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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

运行效果,实现入场动画:
初始:

入场中:
入场完毕:


出场动画

理同入场动画,其中,
v-leave-from表示 出场动画开始时的透明度、
v-leave-active表示过程持续的时延速度(ease -- 缓慢)、
v-leave-to表示出场动画的结束时的透明度;
其余步骤 同入场动画:

<!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;
        }
        .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>
</head>

<body>
    <div id="heheApp"></div>
</body>
<script>
    const app = Vue.createApp({
        data() {
            return {
                show: false
            }
        },
        methods: {
          handleClick() {
            this.show = !this.show;
          }  
        },
        template: `
            <div>
                <transition>
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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

运行效果:
初始:

切换中:
切换结束:


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

两个v-xxx-active可以写到一起,
如果v-leave-from指定的值与v-enter-to相同(一般都是相同的),那也可以省略它;
以下代码实现的效果,同上:

...
        <style>
        .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;
        }
    </style>
 ...


出场入场帧动画

准备好一个keyframes,然后把它写在v-xxx-active中即可:

···
  <style>
        @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;
        }
    </style>
···


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

可以在template中,
<transition>标签中,用name=指定一个命名,
然后在style中定义css类时,
将之前的v-xxx-xxx等定义符换成[命名]-xxx-xxx即可:

<!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 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;
        }
    </style>
    <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;
          }  
        },
        template: `
            <div>
                <transition name="heheda">
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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


对动画类的完全命名

即直接在template中,在<transition>标签中,
使用[原css命名] = [新命名]的方式,对整个CSS类取别名,
用的时候,直接使用新命名即可:

<!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 shake {
            0% {
                transform: translateX(-100px);
            }
            50% {
                transform: translateX(-50px);
            }
            100% {
                transform: translateX(50px);
            }
        }
        .hihi {
            animation: shake 2s;
        }
        .byebye {
            animation: shake 2s;
        }
    </style>
    <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;
          }  
        },
        template: `
            <div>
                <transition 
                    enter-active-class="hihi"
                    leave-active-class="byebye">
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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

关键代码:

<!DOCTYPE html>
<html lang="en">
<head>
...
    <style>
        ...
        .hihi {
            animation: shake 2s;
        }
        .byebye {
            animation: shake 2s;
        }
    </style>
    <script src="https://unpkg.com/vue@next"></script>
</head>

...
<script>
   ...
    template: `
       ...
                <transition 
                    enter-active-class="hihi"
                    leave-active-class="byebye">
                    <div v-if="show">heheda</div>
                </transition>
...
</script>

运行效果同上例;


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

这里以引入Animate.css为例;
官网:https://animate.style/
官网首页如下,右侧列表是各种动画类型,点击可以预览效果:


官网简略引导:
(如下可知,即框架已经把动画定义实现好了,
使用时只用直接使用定义好的框架样式命名即可)

弹跳效果:

<!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>
    <link
        rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
    />
</head>

<body>
    <div id="heheApp"></div>
</body>
<script>
    const app = Vue.createApp({
        data() {
            return {
                show: false
            }
        },
        methods: {
          handleClick() {
            this.show = !this.show;
          }  
        },
        template: `
            <div>
                <transition 
                    enter-active-class="animate__animated animate__bounce"
                    leave-active-class="animate__animated animate__bounce">
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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

闪烁效果:

template: `
            <div>
                <transition 
                    enter-active-class="animate__animated animate__flash"
                    leave-active-class="animate__animated animate__flash">
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`


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

区别在于隐藏组件的底层机制不同而已,
在博客笔记《Vue3 | 条件渲染 与 列表循环渲染》我们讨论过这个事情;


颜色过渡和位移 动画 结合

<!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 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;
        }
    </style>
    <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;
          }  
        },
        template: `
            <div>
                <transition>
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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

关键代码:
入场,从红色开始,到黑色(默认颜色);
出场,从黑色(默认颜色)开始,到红色;
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;
        }


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

<transition>标签上,
写上type="transition",则组合动画的时长 以过渡动画 为准;
写上type="animation,则组合动画的时长 以普通类型动画 为准;

如下案例,写上type="transition"
则组合动画在 过渡动画执行完毕(即3s)之后,
会立即停止 正在运行的 所有动画:

<!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 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;
        }
    </style>
    <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;
          }  
        },
        template: `
            <div>
                <transition type="transition">
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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


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

<transition>标签上,
写上:duration="[动画时长 / ms]",则组合动画的时长 以这里定义的数据 为准,
无论CSS类中,定义了多长的时延:

 template: `
            <div>
                <transition :duration="1000">
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`


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

:duration="[{enter: [入场动画时延], leave:[出场动画时延]}]"

template: `
            <div>
                <transition :duration="{enter: 1000, leave: 3000}">
                    <div v-if="show">heheda</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`


禁用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>

运行效果:


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

<!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;
          },
        },
        template: `
            <div>
                <transition >
                    <div v-if="show">heheda</div>
                    <div v-else="show">lueluelue</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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


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

-- css块上面已经讲过了,这里主要是看mode="in-out"
这里指定为mode="in-out"的话,
则切换时 先进行入场节点的 入场 及其 动画【in】,
再进行退场节点 的 入场 及其 动画【out】;
【缺点:入场 与 退场的节点,在入场动画完毕前,会同框】

-- 如 指定为mode="out-in",则顺序与上相反;
【特性:入场 与 退场的节点 不会同框】

-- appear特性:初始加载节点的时候,就会启动入场动画
没有加这个标签,入场动画需要触发才会启动;

<!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 app = Vue.createApp({
        data() {
            return {
                show: false
            }
        },
        methods: {
          handleClick() {
            this.show = !this.show;
          },
        },
        template: `
            <div>
                <transition mode="out-in" appear>
                    <div v-if="show">heheda</div>
                    <div v-else="show">lueluelue</div>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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


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

基于上例进行小改动即可,其原理是类似的,
以下代码实现的效果 同上例;

<!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 {
                show: false
            }
        },
        methods: {
          handleClick() {
            this.show = !this.show;
          },
        },
        components: {
            'component-a': ComponentA,
            'component-b': ComponentB,
        },
        template: `
            <div>
                <transition mode="out-in" appear>
                    <component-a v-if="show"/>
                    <component-b v-else="show"/>
                </transition>
                <button @click="handleClick">切换</button>
            </div>`
    });

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


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

-- data字段 存储要展示的子组件 的 组件名;
-- 点击事件中 更改data字段 以 更换 展示的子组件,
同时产生组件的 入场退场,触发相关动画;
-- template中 使用 <component>标签占位,
使用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

<!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 {
                number: 1,
                animateNumber: 1
            }
        },
        methods: {
          handleClick() {
            this.number = 10;
            const animation = setInterval(() => {
                this.animateNumber += 1;
                if(this.animateNumber >= this.number){
                    clearInterval(animation);
                }
            }, 100);
          },
        },
        template: `
            <div>
                <span>{{animateNumber}}</span>
                <button @click="handleClick">改变</button>
            </div>`
    });

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

运行效果:


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,373评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,732评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,163评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,700评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,036评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,425评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,737评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,421评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,141评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,398评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,908评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,276评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,907评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,018评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,772评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,448评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,325评论 2 261

推荐阅读更多精彩内容