过渡&&动画描述
- 动画的实现不仅仅可以通过 margin,或者设置 top 来实现。还可以通过 transform,perspective 等来实现。
- 我们希望尽可能对元素动画进行硬件加速。并且使用不触发重绘的 property。
- 我们可以通过工具,如 CSS Triggers[https://csstriggers.com/]来查看哪些属性在动画时会触发重绘
- opacity, transform 不会触发任何几何形状的变化或者绘制。是在 web 上做元素移动的理想选择
- 注入 perspective、backface-visibility 和 transform:translateZ(x)等属性将让浏览器知道你需要硬件加速,如果你需要对一个元素进行硬件加速,可以使用任意一个
进入过渡 && 离开过渡
单元素/组件过渡
- vue 提供了 transition 组件,无需引入。添加至组件可以控制过渡效果
– v-if
– v-for
– component is (动态组件) - 多组件过渡
– 组件根节点
– transition 组件只接受一个组件或者标签。渲染多个元素时 v-if/v-else 最终渲染的也是一个标签
<template>
<transition name="container">
<div v-show="show">show something</div>
</transition>
<button @click="show = !show">toggle show</button>
</template>
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "App",
components: {},
setup(props) {
const show = ref(false);
return {
show,
};
},
});
.container-enter-active,
.container-leave-active {
transition: opacity 0.5s ease;
}
.container-enter-from,
.container-leave-to {
opacity: 0;
}
- 过渡 class(指定具体 name,或者使用默认的 v)
-
[name]-enter-active: 定义进入过渡生效时的状态
-
[name]-leave-active: 定义离开过渡生效时的状态
-
[name]-enter-from: 定义进入过渡的开始状态
-
[name]-leave-from: 定义离开过渡的开始状态
-
[name]-enter-to: 定义进入过渡的结束状态
-
[name]-leave-to: 离开过渡的结束状态。
- transtion 组件接受以下属性来自定义过渡类名
- enter-from-class
- enter-active-class
- enter-to-class…
- transtion 组件接受 transitionend/animationend 监听过渡的完成。取决于 css 规则
<transition name="container" @transitionend="alertcom">
<div v-show="show">show something</div>
</transition>
<button @click="show = !show">toggle show</button>
setup(props) {
const show = ref(false);
const alertcom = () => {
console.log('alertcom')
}
return {
show,
alertcom
};
- transition 组件接受的其他参数(属性或者事件)
-
type: 当前过渡是那种类型。取值范围为 animation/transition-参数
-
duration: 过渡时间-参数
-
before-enter: JS 钩子-事件
-
enter: JS 钩子-事件
- 过渡模式 - mode - 属性
- in-out:新元素先进行过渡,完成之后当前元素过渡离开
- out-in:当前元素先进行过渡,完成之后新元素过渡进入(Tips: 大多数情况下的理想状态)
列表过渡
- 可以结合 v-for 使用
- 使用到 transition-group 组件。可以接收多个组件,列表。不用必须绑定 v-if/v-show
<template>
<button @click="add">Add</button>
<button @click="deleteNum">Delete</button>
<transition-group name="container">
<span v-for="item in NumArr" :key="item" class="list-item">
{{ item }}
</span>
</transition-group>
</template>
import { defineComponent, ref } from "vue";
const useGetRandomIdx = (arr) => {
if (Array.isArray(arr)) {
return Math.floor(Math.random() * arr.length);
}
};
export default defineComponent({
name: "App",
components: {},
setup(props) {
const NumArr = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
const maxNum = ref(10);
const add = () => {
NumArr.value.splice(useGetRandomIdx(NumArr.value), 0, maxNum.value++);
};
const deleteNum = () => {
NumArr.value.splice(useGetRandomIdx(NumArr.value), 1);
};
return {
NumArr,
add,
deleteNum,
};
},
});
button {
margin-right: 20px;
}
.list-item {
margin-right: 10px;
}
.container-enter-active,
.container-leave-active {
transition: opacity 1s ease;
}
.container-enter-from,
.container-leave-to {
opacity: 0;
}
- transtion-group 组件不仅可以进入和离开动画,还可以改变定位。改变定位时,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡。解决这个问题,可以用 v-move (或者[name]-move)设置在改变位置时候的动画。(使用 FLIP 过渡的元素不能设置为 display: inline。可以放到 flex 里或者设置 display: inline-block)
<template>
<button @click="shufer">shufer</button>
<button @click="add">Add</button>
<button @click="deleteItem">deleteItem</button>
<transition-group name="container" tag="div">
<span v-for="item in NumArr" :key="item" class="list-item">
{{ item }}
</span>
</transition-group>
<Child />
</template>
import { defineComponent, ref } from "vue";
import Child from "./components/HelloWorld.vue";
const useGetRandomIdx = (arr) => {
if (Array.isArray(arr)) {
return Math.floor(Math.random() * arr.length);
}
};
export default defineComponent({
name: "App",
components: { Child },
setup(props) {
const NumArr = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
const max = ref(10);
const shufer = () => {
const idx = useGetRandomIdx(NumArr.value);
const start = NumArr.value[idx];
const end = NumArr.value[10 - idx];
NumArr.value[idx] = end;
NumArr.value[10 - idx] = start;
};
const add = () => {
const idx = useGetRandomIdx(NumArr.value);
NumArr.value.splice(idx, 0, max.value++);
};
const deleteItem = () => {
const idx = useGetRandomIdx(NumArr.value);
NumArr.value.splice(idx, 1);
};
return {
NumArr,
shufer,
add,
deleteItem,
};
},
});
button {
margin-right: 20px;
}
.list-item {
transition: all 0.8s ease;
margin-right: 10px;
display: inline-block;
}
.container-enter-from,
.container-leave-to {
opacity: 0;
transform: translateY(30px);
}
.container-leave-active {
position: absolute;
}