vue中css动画原理

# vue中css动画原理

[TOC]

# 一、过渡动画原理

enter princile leave principle

.v-enter,
.v-leave-to {
    opacity: 0;
}

.v-enter-active,
.v-leave-active {
    transition: opacity 3s;
}
1
2
3
4
5
6
7
8
9
<div id="root">
    <transition>
        <div v-if="show">hello world</div>
    </transition>
    <button @click="handleBtnClick">change</button>

</div>
<script>
    var vm = new Vue({
        el: '#root',
        data: {
            show: true
        },
        methods: {
            handleBtnClick: function() {
                this.show = !this.show
            }
        }
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 二、在vue中使用animate.css库

# 2.1 无引入的写法

@keyframes bounce-in {
    0% {
        transform: scale(0);
    }
    50% {
        transform: scale(1.5);
    }
    100% {
        transform: scale(1);
    }
}

.enter {
    transform-origin: left center;
    animation: bounce-in 3s;
}

.leave {
    transform-origin: left center;
    animation: bounce-in 3s reverse;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="root">
    <!-- <transition name="fade" enter-active-class="active" leave-active-class="leave"> -->
    <transition enter-active-class="enter" leave-active-class="leave">
        <div v-if="show">hello world</div>
    </transition>
    <button @click="handleBtnClick">change</button>
</div>

<script>
    var vm = new Vue({
        el: '#root',
        data: {
            show: true
        },
        methods: {
            handleBtnClick: function() {
                this.show = !this.show
            }
        }
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2.2 引入的写法

<style></style>

<div id="root">
    <transition enter-active-class="animate bounceIn" leave-active-class="animate bounceOut">
        <div v-if="show">hello world</div>
    </transition>
    <button @click="handleBtnClick">change</button>
</div>
1
2
3
4
5
6
7
8

# 三、在vue中同时使用过渡和动画

  • 初次显示也有动画
<div id="root">
    <!-- appear使第一次显示也有动画 -->
    <transition appear
                enter-active-class="animate bounceIn"
                leave-active-class="animate bounceOut"
                appear-avtive-class="animate bounceIn">
        <div v-if="show">hello world</div>
    </transition>
    <button @click="handleBtnClick">change</button>
</div>
1
2
3
4
5
6
7
8
9
10
  • 同时过渡和动画并统一时长
.fade-enter,
.fade-leave-to {
    opacity: 0;
}

.fade-enter-active,
.fade-leave-active {
    transition: opacity 5s;
}
1
2
3
4
5
6
7
8
9
<div id="root">
    <!-- 为了避免animate与transiton的动画时长不一致,需统一 -->
    <!-- 第一种:type="transition" -->
    <!-- 第二种::duration="10000" 默认单位:ms -->
    <!-- 第三种 -->
    <transition name="fade"  :duration="{enter:10000,leave:5000}" appear enter-active-class="animate bounceIn fade-enter-active" leave-active-class="animate bounceOut fade-leave-active" appear-avtive-class="animate bounceIn">
        <div v-if="show">hello world</div>
    </transition>
    <button @click="handleBtnClick">change</button>
</div>
1
2
3
4
5
6
7
8
9
10

# 四、vue中的js动画与velocity.js的结合

  • js动画钩子
<div id="root">
    <transition @before-enter="handleBeforeEnter" 
                @enter="handleEnter" 
                @after-enter="handleAfterEnter">
        <div v-show="show">hello world</div>
    </transition>
    <button @click="handleBtnClick">toggle</button>
</div>
<script>
    var vm = new Vue({
        el: '#root',
        data: {
            show: true
        },
        methods: {
            handleBtnClick: function() {
                this.show = !this.show
            },
            //js动画钩子
            //el指transition包裹的元素
            handleBeforeEnter: function(el) {
                el.style.color = 'red';
            },
            //done是回调函数
            handleEnter: function(el, done) {
                setTimeout(() => {
                    el.style.color = 'yellow'
                }, 2000)
                setTimeout(() => {
                    done()
                }, 4000)
            },
            //由done()触发
            handleAfterEnter: function(el) {
                el.style.color = 'blue'
            }
        }
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

出场动画:把enter换成leave即可

  • velocity.js
methods: {
    handleBtnClick: function() {
        this.show = !this.show
    },
        handleBeforeEnter: function(el) {
            el.style.opacity = 0;
        },
            handleEnter: function(el, done) {
                Velocity(el, {
                    opacity: 1
                }, {
                    duration: 1000,
                    complete: done
                })
            },
                handleAfterEnter: function(el) {
                    el.style.color = 'blue'
                }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 五、多个元素或组件的过渡

.v-enter,
.v-leave-to {
    opacity: 0;
}

.v-enter-active,
.v-leave-active {
    transition: opacity 1s;
}
1
2
3
4
5
6
7
8
9

# 5.1 多个元素的过渡动画

<div id="root">
    <!-- mode控制切换模式,这里是先隐藏后显示 -->
    <transition mode="out-in">
        <!-- 添加key是为了避免div显示隐藏时出现dom复用 -->
        <div v-if="show" key="hello">hello world</div>
        <div v-else key="bye">bye world</div>
    </transition>
    <button @click="handleBtnClick">toggle</button>
</div>

<script>
    var vm = new Vue({
        el: '#root',
        data: {
            show: true
        },
        methods: {
            handleBtnClick: function() {
                this.show = !this.show
            }
        }
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 5.2 多个组件的过渡

<div id="root">
    <transition mode="out-in">
        <child-one v-if="show"></child-one>
        <child-two v-else></child-two>
    </transition>
    <button @click="handleBtnClick">toggle</button>
</div>
<script>
    Vue.component('child-one', {
        template: '<div>child-one</div>'
    })

    Vue.component('child-two', {
        template: '<div>child-two</div>'
    })

    var vm = new Vue({
        el: '#root',
        data: {
            show: true
        },
        methods: {
            handleBtnClick: function() {
                this.show = !this.show
            }
        }
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 5.3 动态组件的过渡

<div id="root">
    <transition mode="out-in">
        <component :is="type"></component>
    </transition>
    <button @click="handleBtnClick">toggle</button>
</div>

<script>
    Vue.component('child-one', {
        template: '<div>child-one</div>'
    })

    Vue.component('child-two', {
        template: '<div>child-two</div>'
    })

    var vm = new Vue({
        el: '#root',
        data: {
            type: 'child-one'
        },
        methods: {
            handleBtnClick: function() {
                this.type = this.type === 'child-one' ? 'child-two' : 'child-one'
            }
        }
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 六、列表过渡

<div id="root">
    <transition-group mode="out-in">
        <!-- <transition-group> can only be used on a single element. Use <transition-group> for lists. -->
        <!-- 不建议用index作为key值,一是会导致性能的降低,而是整个功能上可能会产生影响 -->
        <!-- <div v-for="(item, index) of list" :key="index"></div> -->
        <div v-for="item of list" :key="item.id">{{item.title}}</div>
    </transition-group>
    <button @click="handleBtnClick">add</button>
</div>
<script>
    var count = 0;
    var vm = new Vue({
        el: '#root',
        data: {
            list: []
        },
        methods: {
            handleBtnClick: function() {
                this.list.push({
                    id: count++,
                    title: 'hello world'
                })
            }
        }
    })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

不带Key的复用情况是,

(1)当迭代的是数组的时候,是以数组的值作为唯一标识。

(2)当迭代的是个对象的话,就以对象的key作为标识。

当使用key属性来给每个节点做一个唯一标识,复用情况就不一样了,会复用已有的Dom节点元素,而Diff算法还可以正确的识别此新增的节点,并且能快速找到正确的位置区直接插入。

key的主要作用是为了能高效的跟新虚拟DOM

# 七、动画封装

<div id="root">
    <fade :show="show">
        <div>hello world</div>
    </fade>
    <fade :show="show">
        <div>bye world</div>
    </fade>
    <button @click="handleBtnClick">toggle</button>
</div>

<script>
    Vue.component('fade', {
        props: ['show'],
        template: `
<transition @before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter">
<slot v-if="show"></slot>
    </transition>
`,
        methods: {
            handleBeforeEnter: function(el) {
                el.style.color = 'red'
            },
            handleEnter: function(el, done) {
                setTimeout(() => {
                    el.style.color = 'yellow'
                }, 2000)
                setTimeout(() => {
                    done()
                }, 4000)
            },
            handleAfterEnter: function(el) {
                el.style.color = 'blue'
            }
        }
    })
    var vm = new Vue({
        el: '#root',
        data: {
            show: true
        },
        methods: {
            handleBtnClick: function() {
                this.show = !this.show
            }
        }
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49