2024-05-19 21:20:42 +08:00
|
|
|
<script lang="ts" setup>
|
2024-05-25 10:19:41 +08:00
|
|
|
import type { TimeoutHandle } from '@vben/types';
|
|
|
|
|
|
|
|
import { ref, watch } from 'vue';
|
2024-05-19 21:20:42 +08:00
|
|
|
|
|
|
|
interface Props {
|
2024-05-25 10:19:41 +08:00
|
|
|
/**
|
|
|
|
* @zh_CN 最小加载时间
|
|
|
|
* @en_US Minimum loading time
|
|
|
|
*/
|
|
|
|
minLoadingTime?: number;
|
2024-05-19 21:20:42 +08:00
|
|
|
/**
|
|
|
|
* @zh_CN loading状态开启
|
|
|
|
*/
|
2024-05-25 10:19:41 +08:00
|
|
|
spinning?: boolean;
|
2024-05-19 21:20:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
defineOptions({
|
|
|
|
name: 'Spinner',
|
|
|
|
});
|
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
|
minLoadingTime: 200,
|
|
|
|
});
|
|
|
|
const startTime = ref(0);
|
|
|
|
const endTime = ref(0);
|
|
|
|
const showSpinner = ref(false);
|
|
|
|
const timer = ref<TimeoutHandle>();
|
|
|
|
|
|
|
|
watch(
|
|
|
|
() => props.spinning,
|
|
|
|
(show) => {
|
|
|
|
if (!show) {
|
|
|
|
showSpinner.value = false;
|
|
|
|
clearTimeout(timer.value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
startTime.value = performance.now();
|
|
|
|
timer.value = setTimeout(() => {
|
|
|
|
endTime.value = performance.now();
|
|
|
|
|
|
|
|
const loadingTime = endTime.value - startTime.value;
|
|
|
|
|
|
|
|
showSpinner.value = loadingTime > props.minLoadingTime;
|
|
|
|
}, props.minLoadingTime);
|
|
|
|
},
|
|
|
|
{
|
|
|
|
immediate: true,
|
|
|
|
},
|
|
|
|
);
|
2024-05-19 21:20:42 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<div
|
2024-05-25 10:19:41 +08:00
|
|
|
v-if="showSpinner"
|
2024-05-19 21:20:42 +08:00
|
|
|
class="flex-center bg-overlay absolute left-0 top-0 size-full backdrop-blur-sm"
|
|
|
|
>
|
2024-05-25 10:19:41 +08:00
|
|
|
<div class="loader relative h-12 w-12"></div>
|
2024-05-19 21:20:42 +08:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
@import '@vben-core/design/global';
|
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
@keyframes jump-ani {
|
|
|
|
15% {
|
|
|
|
border-bottom-right-radius: 3px;
|
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
25% {
|
|
|
|
transform: translateY(9px) rotate(22.5deg);
|
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
50% {
|
|
|
|
border-bottom-right-radius: 40px;
|
|
|
|
transform: translateY(18px) scale(1, 0.9) rotate(45deg);
|
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
75% {
|
|
|
|
transform: translateY(9px) rotate(67.5deg);
|
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
100% {
|
|
|
|
transform: translateY(0) rotate(90deg);
|
2024-05-19 21:20:42 +08:00
|
|
|
}
|
2024-05-25 10:19:41 +08:00
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
@keyframes shadow-ani {
|
|
|
|
0%,
|
|
|
|
100% {
|
|
|
|
transform: scale(1, 1);
|
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
50% {
|
|
|
|
transform: scale(1.2, 1);
|
2024-05-19 21:20:42 +08:00
|
|
|
}
|
2024-05-25 10:19:41 +08:00
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
.loader {
|
|
|
|
&::before {
|
|
|
|
@apply bg-primary/50 absolute left-0 top-[60px] h-[5px] w-12 animate-[shadow-ani_0.5s_linear_infinite] rounded-[50%] content-[''];
|
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
2024-05-25 10:19:41 +08:00
|
|
|
&::after {
|
|
|
|
@apply bg-primary absolute left-0 top-0 h-full w-full animate-[jump-ani_0.5s_linear_infinite] rounded content-[''];
|
2024-05-19 21:20:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|