FLIP 动画技术实现
FLIP 动画技术是一种实现流畅、高性能动画效果的方法,它将动画分为四个步骤:
- First(第一帧) : 记录动画元素的初始状态(位置、大小等)。
- Last(最后一帧) : 将元素立即切换到动画的最终状态,不应用任何动画效果。
- Invert(反转) : 通过 CSS 变换(transform)将元素从最终状态反转回第一帧的状态。
- Play(播放) : 移除反转效果,此时元素从第一帧状态平滑过渡到最终状态,实现动画效果。
核心代码
<script setup lang="ts">
import { onMounted, ref } from "vue";
const showPanel = ref<boolean>(false);
const panelDom = ref<HTMLDivElement>();
let closeTimer: ReturnType<typeof setTimeout> | null = null;
function changeShowCard() {
if (!showPanel.value) {
// 展开动画
showPanel.value = true;
requestAnimationFrame(() => {
if (panelDom.value) {
const clientRect = panelDom.value.getBoundingClientRect();
panelDom.value.style.height = '0px';
requestAnimationFrame(() => {
if (panelDom.value) {
panelDom.value.classList.add("trans-animate");
panelDom.value.style.height = `${clientRect.height}px`;
}
});
}
});
} else {
// 收起动画
if (panelDom.value) {
const clientRect = panelDom.value.getBoundingClientRect();
panelDom.value.classList.remove("trans-animate");
if (clientRect.height < 100) {
closeTimer && clearTimeout(closeTimer);
panelDom.value.classList.remove("trans-animate");
showPanel.value = false;
} else {
requestAnimationFrame(() => {
if (panelDom.value) {
panelDom.value.style.height = `${clientRect.height}px`;
closeTimer && clearTimeout(closeTimer);
closeTimer = setTimeout(() => {
panelDom.value?.classList.remove("trans-animate");
showPanel.value = false;
closeTimer = null;
}, 700);
requestAnimationFrame(() => {
if (panelDom.value) {
panelDom.value.classList.add("trans-animate");
panelDom.value.style.height = "0px";
}
});
}
});
}
}
}
}
onMounted(() => {
closeTimer && clearTimeout(closeTimer);
});
</script>
代码解释
展开动画
- 将
showPanel.value设置为true。 - 使用
requestAnimationFrame确保在下一次浏览器重绘前执行关键代码。 - 获取元素的初始高度
clientRect.height。 - 将元素高度设置为
0px。 - 在下一次重绘前,添加 CSS 过渡类
trans-animate,并将元素高度设置为目标高度clientRect.height。这时元素会从初始高度0px平滑过渡到目标高度,实现展开动画效果。
收起动画
-
获取元素当前高度
clientRect.height。 -
移除 CSS 过渡类
trans-animate,中断当前动画。 -
如果当前高度小于
100px,直接将高度设置为0px,移除过渡类,设置showPanel.value为false。 -
否则,使用
requestAnimationFrame确保在下一次重绘前执行以下操作:- 将元素高度设置为当前高度
clientRect.height。 - 清除上一次的定时器(如果存在)。
- 设置一个
700ms的定时器,在定时器到期后移除 CSS 过渡类,设置showPanel.value为false。 - 在下一次重绘前,添加 CSS 过渡类
trans-animate,并将元素高度设置为0px。这时元素会从当前高度平滑过渡到0px,实现收起动画效果。
- 将元素高度设置为当前高度
其他细节
- 在组件挂载时,使用
onMounted钩子清除可能存在的定时器,避免内存泄漏。 - 使用
requestAnimationFrame确保关键代码在浏览器重绘前执行,保证视觉效果的流畅性。 - 通过设置元素高度和添加/移除 CSS 过渡类来控制动画效果。
注意事项
- 性能问题: 获取元素大小/位置信息以及强制浏览器重绘都会带来一定性能开销,需要适当优化。
- 动画中断: 如果在动画播放过程中再次触发动画,需要处理动画中断的情况。
- 动画方向: 根据实际需求,可能需要处理动画的方向(如垂直/水平展开)。
- 嵌套动画: 如果动画元素内包含了其他子元素,可能需要额外处理子元素的状态变化。
- 定时器管理: 使用定时器时要注意清除,避免内存泄漏。