APIs
LoadingBar
参数 |
说明 |
类型 |
默认值 |
containerClass |
加载条容器的类名 |
string |
undefined |
containerStyle |
加载条容器的样式 |
CSSProperties |
{} |
loadingBarSize |
加载条大小,单位 px |
number |
2 |
colorLoading |
加载中颜色 |
string |
‘#1677ff’ |
colorFinish |
加载完成颜色 |
string |
‘#1677ff’ |
colorError |
加载错误颜色 |
string |
‘#ff4d4f’ |
to |
加载条的挂载位置,可选:元素标签名(例如 body )或者元素本身 |
string | HTMLElement |
‘body’ |
Methods
名称 |
说明 |
类型 |
start |
开始加载的回调函数 |
(from = 0, to = 80) => void |
finish |
结束加载的回调函数 |
() => void |
error |
出现错误的回调函数 |
() => void |
创建加载条组件LoadingBar.vue
<script setup lang="ts">
import { ref, nextTick } from 'vue'
import type { CSSProperties } from 'vue'
interface Props {
containerClass?: string // 加载条容器的类名
containerStyle?: CSSProperties // 加载条容器的样式
loadingBarSize?: number // 加载条大小,单位 px
colorLoading?: string // 加载中颜色
colorFinish?: string // 加载完成颜色
colorError?: string // 加载错误颜色
to?: string | HTMLElement // 加载条的挂载位置,可选:元素标签名(例如 body)或者元素本身
}
withDefaults(defineProps<Props>(), {
containerClass: undefined,
containerStyle: () => ({}),
loadingBarSize: 2,
colorLoading: '#1677ff',
colorFinish: '#1677ff',
colorError: '#ff4d4f',
to: 'body'
})
const showLoadingBar = ref(false)
const loadingBarRef = ref() // 加载条 DOM 引用
const loadingStarted = ref(false) // 加载条是否开始
const loadingFinishing = ref(false) // 加载条是否完成
const loadingErroring = ref(false) // 加载条是否报错
async function init() {
showLoadingBar.value = false
loadingFinishing.value = false
loadingErroring.value = false
}
async function start(from = 0, to = 80, status: 'starting' | 'error' = 'starting') {
// 加载条开始加载的回调函数
loadingStarted.value = true
await init()
if (loadingFinishing.value) {
return
}
showLoadingBar.value = true
await nextTick()
if (!loadingBarRef.value) {
return
}
loadingBarRef.value.style.transition = 'none' // 禁用过渡
loadingBarRef.value.style.maxWidth = `${from}%`
void loadingBarRef.value.offsetWidth // 触发浏览器回流(重排)
loadingBarRef.value.className = `loading-bar loading-bar-${status}`
loadingBarRef.value.style.transition = ''
loadingBarRef.value.style.maxWidth = `${to}%`
}
async function finish() {
// 加载条结束加载的回调函数
if (loadingFinishing.value || loadingErroring.value) {
return
}
if (loadingStarted.value) {
await nextTick()
}
loadingFinishing.value = true
if (!loadingBarRef.value) {
return
}
loadingBarRef.value.className = 'loading-bar loading-bar-finishing'
loadingBarRef.value.style.maxWidth = '100%'
void loadingBarRef.value.offsetWidth // 触发浏览器回流(重排)
showLoadingBar.value = false
}
function error() {
// 加载条出现错误的回调函数
if (loadingFinishing.value || loadingErroring.value) {
return
}
if (!showLoadingBar.value) {
void start(100, 100, 'error').then(() => {
loadingErroring.value = true
})
} else {
loadingErroring.value = true
if (!loadingBarRef.value) {
return
}
loadingBarRef.value.className = 'loading-bar loading-bar-error'
loadingBarRef.value.style.maxWidth = '100%'
void loadingBarRef.value.offsetWidth
showLoadingBar.value = false
}
}
function onAfterEnter() {
if (loadingErroring.value) {
showLoadingBar.value = false
}
}
async function onAfterLeave() {
await init()
}
defineExpose({
start,
finish,
error
})
</script>
<template>
<Teleport :disabled="!to" :to="to">
<Transition name="fade-in" @after-enter="onAfterEnter" @after-leave="onAfterLeave">
<div v-show="showLoadingBar" class="m-loading-bar-container" :class="containerClass" :style="containerStyle">
<div
ref="loadingBarRef"
class="loading-bar"
:style="`--loading-bar-size: ${loadingBarSize}px; --color-loading: ${colorLoading}; --color-finish: ${colorFinish}; --color-error: ${colorError}; max-width: 100%;`"
></div>
</div>
</Transition>
</Teleport>
</template>
<style lang="less" scoped>
.fade-in-enter-active {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.fade-in-leave-active {
transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
}
.fade-in-enter-from,
.fade-in-leave-to {
opacity: 0;
}
.m-loading-bar-container {
z-index: 9999;
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--loading-bar-size);
.loading-bar {
width: 100%;
transition:
max-width 4s linear,
background 0.2s linear;
height: var(--loading-bar-size);
border-radius: var(--loading-bar-size);
}
.loading-bar-starting {
background: var(--color-loading);
}
.loading-bar-finishing {
background: var(--color-finish);
transition:
max-width 0.2s linear,
background 0.2s linear;
}
.loading-bar-error {
background: var(--color-error);
transition:
max-width 0.2s linear,
background 0.2s linear;
}
}
</style>
在要使用的页面引入
其中引入使用了以下组件:
<script setup lang="ts">
import LoadingBar from './LoadingBar.vue'
import { ref } from 'vue'
const loadingBar = ref()
const disabled = ref(true)
const localCardRef = ref()
const localLoadingBar = ref()
const customLoadingBar = ref()
function handleStart() {
loadingBar.value.start()
disabled.value = false
}
function handleFinish() {
loadingBar.value.finish()
disabled.value = true
}
function handleError() {
disabled.value = true
loadingBar.value.error()
}
</script>
<template>
<div>
<h1>{
{ $route.name }} {
{ $route.meta.title }}</h1>
<h2 class="mt30 mb10">基本使用</h2>
<Space>
<Button type="primary" @click="handleStart">开始</Button>
<Button :disabled="disabled" @click="handleFinish">结束</Button>
<Button type="danger" @click="handleError">报个错</Button>
</Space>
<LoadingBar ref="loadingBar" />
<h2 class="mt30 mb10">局部加载条</h2>
<div class="m-container" ref="localCardRef">
<Space>
<Button type="primary" @click="localLoadingBar.start()">Start</Button>
<Button @click="localLoadingBar.finish()">Finish</Button>
<Button type="danger" @click="localLoadingBar.error()">Error</Button>
</Space>
</div>
<LoadingBar ref="localLoadingBar" :container-style="{ position: 'absolute' }" :to="localCardRef" />
<h2 class="mt30 mb10">自定义加载条样式</h2>
<Space>
<Button type="primary" @click="customLoadingBar.start()">Start</Button>
<Button @click="customLoadingBar.finish()">Finish</Button>
<Button type="danger" @click="customLoadingBar.error()">Error</Button>
</Space>
<LoadingBar
ref="customLoadingBar"
:loading-bar-size="5"
color-loading="#2db7f5"
color-finish="#52c41a"
color-error="magenta"
/>
</div>
</template>
<style lang="less" scoped>
.m-container {
position: relative;
display: flex;
align-items: center;
height: 200px;
padding: 16px 24px;
border: 1px solid #d9d9d9;
}
</style>