transition-group

transition只能过渡单个节点,或多个但每次只渲染一个。不能同时渲染多个节点。

假设需要用v-for渲染一整个列表,这种场景就不能使用transition,需要使用transition-group。

transition-group组件的特点:

  • 默认不渲染一个包裹元素,但可以通过tag指定渲染一个包裹元素
  • 过渡模式不可用,因为不再相互切换特有的元素
  • 内部元素总是需要提供唯一的key值
  • CSS过渡的类将会应用到内部的元素重,而非这个组/容器本身
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
<template>
<div class="content">
<button @click="list.push(list.length+1)">ADD</button> <button @click="list.pop">POP</button>
<div class="wraps">
<!-- 为tag属性赋值,渲染时会为内容加上对应的标签包裹,不写就没有 -->
<!-- 自定义类名属性、生命周期都和transition一样 -->
<transition-group
enter-active-class="animate__animated animate__bounceIn"
leave-active-class="animate__animated animate__hinge">
<div class="item" v-for="(item, index) in list" :key="index">{{ item }}</div>
</transition-group>
</div>
</div>
</template>

<script setup lang="ts">
import { reactive } from "vue"
import "animate.css"

const list = reactive<number[]>([
1, 2, 3, 4, 5, 6
])
</script>

<style lang="scss">
.wraps {
display: flex;
flex-wrap: wrap;
word-break: break-all;
border: 1px solid #ccc;
.item {
margin: 10px;
font-size: 30px;
}
}
</style>

如果给transition-grop加上tag值(例如<transition-group tag="section">省略</transition-group>):

加上了包裹元素section

列表的移动过渡

除了进入和离开外,transition-group还可以使用v-move为定位的改变添加动画。

和transition的类名一样,可以使用name定义前缀,或使用move-class自定义使用的类名。

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
<template>
<div>
<button @click="random">random</button>
<transition-group move-class="move" class="warps" tag="div">
<div class="item" v-for="item in list" :key="item.id">{{ item.number }}</div>
</transition-group>
</div>
</template>

<script setup lang="ts">
import { ref } from "vue"
import _ from "lodash"

// 使用new Array(81)会生成81项都为empty的数组
// 这种方式就会生成81项都是undefined的数组
const list = ref(Array.apply(null,{length:81} as number[]).map((_,index)=>{
return {
id:index,
number:(index%9)+1
}
}))

const random = ()=> {
list.value = _.shuffle(list.value)
}
</script>

<style lang="scss">
.warps {
display: flex;
flex-wrap: wrap;
width: calc(27px * 9);
.item {
width: 25px;
height: 25px;
border: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
}
}
.move {
transition: all 0.5s ease;
}
</style>

状态过渡

也可以给数字、SVG、背景颜色等添加过渡动画。

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
<template>
<div>
<input v-model="num.current" type="number" step="20">
<div>
{{ num.tweenedNumber.toFixed(0) }}
</div>
</div>
</template>

<script setup lang="ts">
import { reactive, watch } from "vue"
import gsap from "gsap"

const num = reactive({
current:0,
tweenedNumber:0
})

watch(()=> num.current, (newVal)=> {
gsap.to(num,{
duration:1,
tweenedNumber:newVal
})
})
</script>