全局 loading 效果
要实现的功能
语法
window.$bus.emit('spin', param, callback)
第一参数
固定字符串 'spin'
第二参数
- 不传值:默认开启 loading
- 传值
true
:开启 loading - 传值
false
:关闭 loading - 传值字符串:修改 loading 文本
- 传值对象:包含 show 默认为
true
;text 默认为'加载中...'
;delay 默认为5000
毫秒
第三参数
callback 是关闭 loading 后的回调
实战效果演示
window.$bus.emit('spin')
window.$bus.emit('spin', 'loading')
window.$bus.emit('spin', '加载中')
window.$bus.emit('spin', { delay: 8000 })
window.$bus.emit('spin', false)
window.$bus.emit('spin', { show: false })
利用 mitt 实现全局 loading 效果
1. 编写 loading.vue
新建文件 loading.vue
,在其中编写一个全局 loading 效果组件。
vue
<script setup lang="ts">
import { ref, onMounted } from "vue";
const spinShow = ref(false);
const spinTxt = ref("加载中...");
type Timeout = ReturnType<typeof setTimeout> | null;
const timer = ref<Timeout>(null);
onMounted(() => {
const bus = (window as any).$bus;
bus.off("spin");
interface Iparam {
text?: string;
show?: boolean;
delay?: number;
callback?: () => void;
}
// 通过调用 window.$bus.emit('spin') 来触发加载中效果,参数为可选
bus.on("spin", (data: string | boolean | Iparam = "加载中...") => {
if (typeof data === "boolean") {
spinShow.value = data;
} else if (typeof data === "string") {
spinShow.value = true;
spinTxt.value = data;
} else if (typeof data === "object") {
const { text = "加载中...", show = true } = data;
spinShow.value = show;
spinTxt.value = text;
}
const callback =
(typeof data === "object" ? data.callback : "") || (() => {});
const delay = (typeof data === "object" ? data.delay : "") || 5000;
if (data) {
clearTimeout(timer.value!);
timer.value = setTimeout(() => {
spinShow.value = false;
callback();
}, delay);
} else {
clearTimeout(timer.value!);
timer.value = setTimeout(() => {
spinShow.value = false;
callback();
}, 500);
}
});
});
</script>
vue
<template>
<div class="zc-spin" v-if="spinShow">
<div class="zc-spin__content">
<div class="zc-spin--loading">
<svg class="circular" viewBox="0 0 50 50">
<circle class="path" cx="25" cy="25" r="20" fill="none"></circle>
</svg>
</div>
<div class="zc-spin--data">{{ spinTxt }}</div>
</div>
</div>
</template>
vue
<style lang="scss">
@keyframes loading-rotate {
to {
transform: rotate(360deg);
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
to {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
// 定义变量, 主要是用于自定义主题, 默认浅色主题, 即 color-scheme: light;
:root {
--zc-color-primary: #65b1ff;
--zc-mask-color: rgba(255, 255, 255, 0.9);
}
// 深色主题, 即 color-scheme: dark;
html.dark {
--zc-color-primary: #3e73ac;
--zc-mask-color: rgba(0, 0, 0, 0.8);
}
.zc-spin {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: var(--zc-mask-color);
z-index: 99;
.zc-spin__content {
position: absolute;
top: 50%;
left: 50%;
width: 300px;
height: 200px;
margin-left: -150px;
margin-top: -100px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.3);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
text-align: center;
transition: opacity 0.2s;
}
.zc-spin--loading {
top: 50%;
margin-top: calc(-40px / 2);
width: 100%;
text-align: center;
position: absolute;
.circular {
display: inline;
height: 40px;
width: 40px;
animation: loading-rotate 2s linear infinite;
.path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #65b1ff;
stroke-linecap: round;
}
}
}
.zc-spin--data {
margin-top: 125px;
color: var(--zc-color-primary);
}
}
</style>
2. 在 App.vue 中引入 loading.vue
vue
<script setup>
import Loading from "./components/loading.vue";
</script>
<template>
<div>
<Loading></Loading>
</div>
</template>
3. 测试 loading
在需要使用 loading 的页面中,引入 Loading 组件,并调用其 show() 方法即可。
vue
<script setup lang="ts">
const doLoading = (text?: string | boolean | object) => {
const btns = document.querySelectorAll(".test-box button");
btns.forEach((e) => {
e.style.zIndex = "9999";
e.style.position = "relative";
});
let param: { [key: string]: any } = {};
if (typeof text === "boolean") param.show = text;
if (typeof text === "string") param.text = text;
if (typeof text === "object") param = text;
window.$bus.emit("spin", {
...param,
callback: () => {
btns.forEach((e) => (e.style.zIndex = ""));
},
});
};
</script>
html
<template>
<div class="test-box">
<p>
<el-button type="primary" @click="doLoading()">spin</el-button
>window.$bus.emit('spin')
</p>
<p>
<el-button type="primary" @click="doLoading('loading')">loading</el-button
>window.$bus.emit('spin', 'loading')
</p>
<p>
<el-button type="primary" @click="doLoading('加载中')">加载中</el-button
>window.$bus.emit('spin', '加载中')
</p>
<p>
<el-button type="primary" @click="doLoading({ delay: 8000 })"
>{ delay: 8000 }</el-button
>window.$bus.emit('spin', { delay: 8000 })
</p>
<p>
<el-button type="danger" @click="doLoading(false)">false</el-button
>window.$bus.emit('spin', false)
</p>
<p>
<el-button type="danger" @click="doLoading({ show: false })"
>{ show: false }</el-button
>window.$bus.emit('spin', { show: false })
</p>
</div>
</template>