场景
预加载的过程中,数据还未请求到,dom已经渲染出来了?
展示效果
实现
封装指令(代码块1)
- app引入(代码块2)
- 使用(代码块3)
代码
封装
import { reactive, watchEffect, h, render } from 'vue';
const state = reactive({
loading: false,
list: []
});
// 动态插入 CSS 样式
const insertStyles = () => {
const style = document.createElement('style');
style.textContent = `
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.frame-container {
animation: fadeIn 0.5s ease-in-out;
}
.frame-container.fade-out {
animation: fadeOut 0.5s ease-in-out;
}
.frame-item {
background: #e5e5e5;
border-radius: 4px;
}
`;
document.head.appendChild(style);
};
insertStyles(); // 执行插入样式
watchEffect(() => {
const children = state.list.map((el) => {
const rect = el.getBoundingClientRect();
return h('div', {
class: 'frame-item',
style: {
position: 'absolute',
top: rect.top + 'px',
left: rect.left + 'px',
width: '0px', // 初始化宽度为0
height: rect.height + 'px',
borderRadius: getComputedStyle(el).borderRadius,
maxWidth: rect.width + 'px', // 确保动画结束时的最大宽度
},
});
});
const container = h('div', { class: 'frame-container' }, children);
if (state.loading) {
render(container, document.body);
// 动态触发动画
requestAnimationFrame(() => {
state.list.forEach((el, index) => {
const rect = el.getBoundingClientRect();
const frameItem = document.querySelectorAll('.frame-item')[index];
frameItem.style.width = rect.width + 'px'; // 动态设置最终宽度
frameItem.style.transition = 'width 1.5s ease-in-out'; // 过渡动画
});
});
} else {
const existingContainer = document.querySelector('.frame-container');
if (existingContainer) {
existingContainer.classList.add('fade-out');
setTimeout(() => {
render(null, document.body);
}, 300);
}
}
});
const Frame = {
mounted(el, binding) {
state.loading = binding.value;
},
updated(el, binding) {
state.loading = binding.value;
},
unmounted(el) {
state.loading = false;
}
};
const FrameItem = {
mounted(el) {
state.list.push(el);
},
unmounted(el) {
const i = state.list.indexOf(el);
if (i !== -1) {
state.list.splice(i, 1);
}
}
};
const vFrame = {
install: (app) => {
app.directive('frame', Frame);
app.directive('frame-item', FrameItem);
}
};
export default vFrame;
使用
<template>
<div class="mb100" v-frame="frameLoading" :style="{position:'relative',display:'flex',flexDirection:'column',rowGap:'10px'}">
<div :style="{width:'600px'}">
<div v-frame-item>
91273123812738127831738127381273812
</div>
</div>
<div :style="{width:'200px'}">
<div v-frame-item>
012931293
</div>
</div>
<div :style="{width:'300px'}">
<div v-frame-item>
012931293
</div>
</div>
<div :style="{width:'400px'}">
<div v-frame-item>
012931293
</div>
</div>
</div>
</template>
<script setup>
const frameLoading = ref(true)
</script>