3 changed files with 73 additions and 62 deletions
@ -1,97 +1,108 @@ |
|||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||
import { useNamespace } from '@vben-core/toolkit'; |
import type { TimeoutHandle } from '@vben/types'; |
||||
|
|
||||
|
import { ref, watch } from 'vue'; |
||||
|
|
||||
interface Props { |
interface Props { |
||||
|
/** |
||||
|
* @zh_CN 最小加载时间 |
||||
|
* @en_US Minimum loading time |
||||
|
*/ |
||||
|
minLoadingTime?: number; |
||||
/** |
/** |
||||
* @zh_CN loading状态开启 |
* @zh_CN loading状态开启 |
||||
*/ |
*/ |
||||
spinning: boolean; |
spinning?: boolean; |
||||
} |
} |
||||
|
|
||||
defineOptions({ |
defineOptions({ |
||||
name: 'Spinner', |
name: 'Spinner', |
||||
}); |
}); |
||||
|
|
||||
defineProps<Props>(); |
const props = withDefaults(defineProps<Props>(), { |
||||
|
minLoadingTime: 200, |
||||
const { b, e } = useNamespace('spinner'); |
}); |
||||
|
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, |
||||
|
}, |
||||
|
); |
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<div |
<div |
||||
:class="[b(), !spinning ? 'hidden' : '']" |
v-if="showSpinner" |
||||
class="flex-center bg-overlay absolute left-0 top-0 size-full backdrop-blur-sm" |
class="flex-center bg-overlay absolute left-0 top-0 size-full backdrop-blur-sm" |
||||
> |
> |
||||
<div :class="e('loader')"></div> |
<div class="loader relative h-12 w-12"></div> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
@import '@vben-core/design/global'; |
@import '@vben-core/design/global'; |
||||
|
|
||||
@include b('spinner') { |
@keyframes jump-ani { |
||||
@keyframes jump-ani { |
15% { |
||||
15% { |
border-bottom-right-radius: 3px; |
||||
border-bottom-right-radius: 3px; |
} |
||||
} |
|
||||
|
|
||||
25% { |
25% { |
||||
transform: translateY(9px) rotate(22.5deg); |
transform: translateY(9px) rotate(22.5deg); |
||||
} |
} |
||||
|
|
||||
50% { |
50% { |
||||
border-bottom-right-radius: 40px; |
border-bottom-right-radius: 40px; |
||||
transform: translateY(18px) scale(1, 0.9) rotate(45deg); |
transform: translateY(18px) scale(1, 0.9) rotate(45deg); |
||||
} |
} |
||||
|
|
||||
75% { |
75% { |
||||
transform: translateY(9px) rotate(67.5deg); |
transform: translateY(9px) rotate(67.5deg); |
||||
} |
} |
||||
|
|
||||
100% { |
100% { |
||||
transform: translateY(0) rotate(90deg); |
transform: translateY(0) rotate(90deg); |
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
@keyframes shadow-ani { |
@keyframes shadow-ani { |
||||
0%, |
0%, |
||||
100% { |
100% { |
||||
transform: scale(1, 1); |
transform: scale(1, 1); |
||||
} |
} |
||||
|
|
||||
50% { |
50% { |
||||
transform: scale(1.2, 1); |
transform: scale(1.2, 1); |
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
@include e('loader') { |
.loader { |
||||
position: relative; |
&::before { |
||||
width: 48px; |
@apply bg-primary/50 absolute left-0 top-[60px] h-[5px] w-12 animate-[shadow-ani_0.5s_linear_infinite] rounded-[50%] content-['']; |
||||
height: 48px; |
} |
||||
|
|
||||
&::before { |
|
||||
position: absolute; |
|
||||
top: 60px; |
|
||||
left: 0; |
|
||||
width: 48px; |
|
||||
height: 5px; |
|
||||
content: ''; |
|
||||
background: hsl(var(--color-primary) / 50%); |
|
||||
border-radius: 50%; |
|
||||
animation: shadow-ani 0.5s linear infinite; |
|
||||
} |
|
||||
|
|
||||
&::after { |
&::after { |
||||
position: absolute; |
@apply bg-primary absolute left-0 top-0 h-full w-full animate-[jump-ani_0.5s_linear_infinite] rounded content-['']; |
||||
top: 0; |
|
||||
left: 0; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
content: ''; |
|
||||
background: hsl(var(--color-primary)); |
|
||||
border-radius: 4px; |
|
||||
animation: jump-ani 0.5s linear infinite; |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
Loading…
Reference in new issue