APIs
LoadingBar
参数 | 说明 | 类型 | 默认值 | 必传 |
---|
containerClass | 加载条容器的类名 | string | undefined | false |
containerStyle | 加载条容器的样式 | CSSProperties | {} | false |
loadingBarSize | 加载条大小,单位 px | number | 2 | false |
colorLoading | 加载中颜色 | string | ‘#1677ff’ | false |
colorFinish | 加载完成颜色 | string | ‘#1677ff’ | false |
colorError | 加载错误颜色 | string | ‘#ff4d4f’ | false |
to | 加载条的挂载位置,可选:元素标签名(例如 body )或者元素本身 | string | HTMLElement | ‘body’ | false |
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
colorLoading?: string
colorFinish?: string
colorError?: string
to?: string | HTMLElement
}
withDefaults(defineProps<Props>(), {
containerClass: undefined,
containerStyle: () => ({}),
loadingBarSize: 2,
colorLoading: '#1677ff',
colorFinish: '#1677ff',
colorError: '#ff4d4f',
to: 'body'
})
const showLoadingBar = ref(false)
const loadingBarRef = ref()
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>
其中引入使用了以下组件:
- Vue3间距(Space)
- Vue3按钮(Button)
在要使用的页面引入
<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>