Loading-pro
加载数据时显示动画。
基础用法 - 盒子内
<!-- @format -->
<template>
<el-table
v-loadingPro="{
showLoading: true,
animation: 'spinner',
}"
:data="tableData"
style="width: 100%"
>
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const tableData = [
{
date: '2016-05-02',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
{
date: '2016-05-04',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
{
date: '2016-05-01',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
]
</script>
<style>
body {
margin: 0;
}
.example-showcase .el-loading-mask {
z-index: 9;
}
</style>
自定义样式和背景色
showLoading
、text、animation、backgroundColor、borderColor、color、width、height
<!-- @format -->
<template>
<el-table
v-loadingPro="{
showLoading: true,
animation: 'pulse',
}"
element-loading-text="Loading..."
:element-loading-spinner="svg"
element-loading-svg-view-box="-10, -10, 50, 50"
element-loading-background="rgba(122, 122, 122, 0.8)"
:data="tableData"
style="width: 100%"
>
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
<el-table
v-loading-pro="{
showLoading: true,
animation: 'bee',
background: 'rgba(122, 122, 122, 0.8)',
text: 'Loading...',
color: 'pink',
}"
:element-loading-svg="svg"
class="custom-loading-svg"
element-loading-svg-view-box="-10, -10, 50, 50"
:data="tableData"
style="width: 100%"
>
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const loading = ref(true)
const svg = `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`
const tableData = [
{
date: '2016-05-02',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
{
date: '2016-05-04',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
{
date: '2016-05-01',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
]
</script>
<style>
.example-showcase .el-loading-mask {
z-index: 9;
}
</style>
全屏 loading
Show a full screen animation while loading data.
<!-- @format -->
<template>
<el-button type="primary" @click="openFullScreen2"> As a service </el-button>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ElLoadingPro } from 'element-plus'
const fullscreenLoading = ref(false)
const openFullScreen2 = () => {
const loading = ElLoadingPro.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
animation: 'dots',
})
setTimeout(() => {
loading.close()
}, 2000)
}
</script>
Loading Pro 样式展示
告别单调乏味的加载等待!欢迎来到 Loading Pro 的样式展示区。这里汇聚了 Loading Pro 丰富多样的视觉呈现,旨在证明一个加载组件不仅能传递“正在加载”的信息,更能成为你应用界面中一道亮丽且富有格调的风景线。
Loading Pro 样式展示
展示所有35种可爱的loading动画效果
基础样式 (1-17)
可爱样式 (18-35)
服务调用示例
随机样式演示
点击按钮体验随机loading效果
<!-- @format -->
<template>
<div class="loading-styles-showcase">
<h2>Loading Pro 样式展示</h2>
<p>展示所有35种可爱的loading动画效果</p>
<!-- 基础样式组 -->
<div class="style-group">
<h3>基础样式 (1-17)</h3>
<div class="grid-container">
<div
v-for="(style, index) in basicStyles"
:key="style"
class="loading-item"
>
<div class="loading-demo">
<el-card
v-loadingPro="{
showLoading: true,
animation: style,
}"
:element-loading-animation="style"
element-loading-text="Loading..."
class="demo-card"
>
<div class="card-content">{{ index + 1 }}. {{ style }}</div>
</el-card>
</div>
<div class="style-name">{{ style }}</div>
</div>
</div>
</div>
<!-- 可爱样式组 -->
<div class="style-group">
<h3>可爱样式 (18-35)</h3>
<div class="grid-container">
<div
v-for="(style, index) in cuteStyles"
:key="style"
class="loading-item"
>
<div class="loading-demo">
<el-card
v-loadingPro="{
showLoading: true,
animation: style,
}"
:element-loading-animation="style"
element-loading-text="Loading..."
class="demo-card"
>
<div class="card-content">{{ index + 18 }}. {{ style }}</div>
</el-card>
</div>
<div class="style-name">{{ style }}</div>
</div>
</div>
</div>
<!-- 服务调用示例 -->
<div class="style-group">
<h3>服务调用示例</h3>
<div class="service-buttons">
<el-button
v-for="style in allStyles.slice(0, 10)"
:key="style"
type="primary"
class="service-btn"
@click="showServiceLoading(style)"
>
{{ style }} 全屏
</el-button>
</div>
<div class="service-buttons">
<el-button
v-for="style in allStyles.slice(10, 20)"
:key="style"
type="success"
class="service-btn"
@click="showServiceLoading(style)"
>
{{ style }} 全屏
</el-button>
</div>
<div class="service-buttons">
<el-button
v-for="style in allStyles.slice(20, 30)"
:key="style"
type="warning"
class="service-btn"
@click="showServiceLoading(style)"
>
{{ style }} 全屏
</el-button>
</div>
<div class="service-buttons">
<el-button
v-for="style in allStyles.slice(30)"
:key="style"
type="danger"
class="service-btn"
@click="showServiceLoading(style)"
>
{{ style }} 全屏
</el-button>
</div>
</div>
<!-- 随机样式演示 -->
<div class="style-group">
<h3>随机样式演示</h3>
<el-button type="info" size="large" @click="showRandomLoading">
🎲 随机Loading效果
</el-button>
<p class="random-tip">点击按钮体验随机loading效果</p>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ElLoadingPro } from 'element-plus'
// 基础样式 (1-17)
const basicStyles = [
'spinner',
'pulse',
'dots',
'bounce',
'cube',
'gradient',
'heartbeat',
'spiral',
'blink',
'swing',
'breath',
'star',
]
// 可爱样式 (18-35)
const cuteStyles = [
'kitty',
'rainbow',
'love',
'duck',
'bee',
'bubble',
'rocket',
'candy',
'flower',
'butterfly',
'fish',
'sun',
'moon',
'cloud',
'lightning',
'snowflake',
'leaf',
'cherry',
]
// 所有样式
const allStyles = [...basicStyles, ...cuteStyles]
// 显示服务loading
const showServiceLoading = (animation: string) => {
console.log('showServiceLoading', animation)
const loading = ElLoadingPro.service({
lock: true,
text: `${animation} Loading...`,
animation,
background: 'rgba(0, 0, 0, 0.7)',
})
setTimeout(() => {
loading.close()
}, 3000)
}
// 显示随机loading
const showRandomLoading = () => {
const randomStyle = allStyles[Math.floor(Math.random() * allStyles.length)]
const loading = ElLoadingPro.service({
lock: true,
text: `🎉 ${randomStyle} 随机效果!`,
animation: randomStyle,
background: 'rgba(0, 0, 0, 0.8)',
})
setTimeout(() => {
loading.close()
}, 4000)
}
</script>
<style scoped>
.loading-styles-showcase {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.loading-styles-showcase h2 {
text-align: center;
color: #409eff;
margin-bottom: 10px;
font-size: 28px;
}
.loading-styles-showcase p {
text-align: center;
color: #666;
margin-bottom: 30px;
font-size: 16px;
}
.style-group {
margin-bottom: 40px;
}
.style-group h3 {
color: #303133;
margin-bottom: 20px;
font-size: 20px;
border-left: 4px solid #409eff;
padding-left: 10px;
}
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.loading-item {
text-align: center;
}
.loading-demo {
margin-bottom: 10px;
}
.demo-card {
height: 120px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
transition: all 0.3s ease;
}
.demo-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.card-content {
font-size: 14px;
color: #666;
font-weight: 500;
}
.style-name {
font-size: 14px;
color: #409eff;
font-weight: 600;
background: #f0f9ff;
padding: 5px 10px;
border-radius: 4px;
border: 1px solid #e1f5fe;
}
.service-buttons {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 15px;
}
.service-btn {
min-width: 120px;
font-size: 12px;
}
.random-tip {
margin-top: 10px;
font-size: 14px;
color: #909399;
text-align: center;
}
/* 响应式设计 */
@media (max-width: 768px) {
.grid-container {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
}
.demo-card {
height: 100px;
}
.service-buttons {
justify-content: center;
}
.service-btn {
min-width: 100px;
font-size: 11px;
}
}
/* loading遮罩层样式优化 */
:deep(.el-loading-mask) {
border-radius: 8px;
}
:deep(.el-loading-spinner) {
margin-top: -20px;
}
:deep(.el-loading-text) {
margin-top: 10px;
font-size: 12px;
}
</style>
Loading Pro 性能展示
欢迎来到果锅的 Loading Pro 性能展示区!在这里,我们不仅仅是展示一个加载组件,更是向你证明我们对速度和效率的极致追求。你可能会好奇,一个加载组件的性能究竟有多重要?答案是:至关重要。一个卡顿、延迟的加载体验,会瞬间破坏用户对整个应用的信任感。
⚡ Loading Pro 性能展示
性能对比、最佳实践和使用建议
轻量级场景
适用于快速响应的场景,如表单提交、简单查询等
用户体验优先
适用于需要提升用户体验的场景,如首页加载、重要操作等
高性能要求
适用于性能敏感的场景,如大数据表格、实时更新等
移动端优化
适用于移动设备,考虑电池和性能限制
专业、稳重,符合企业形象
动感、有趣,增强游戏体验
温馨、友好,提升购物体验
可爱、生动,吸引学习兴趣
专业、关怀,符合医疗主题
自然、环保,传达绿色理念
<!-- @format -->
<template>
<div class="performance-showcase">
<div class="showcase-header">
<h1>⚡ Loading Pro 性能展示</h1>
<p>性能对比、最佳实践和使用建议</p>
</div>
<!-- 性能对比测试 -->
<el-card class="performance-test">
<template #header>
<div class="card-header">
<span>🏃♂️ 性能对比测试</span>
<el-button :loading="testing" @click="runPerformanceTest"
>开始测试</el-button
>
</div>
</template>
<div class="test-results">
<el-table :data="performanceResults" stripe>
<el-table-column prop="style" label="样式名称" width="120" />
<el-table-column prop="category" label="分类" width="100" />
<el-table-column prop="renderTime" label="渲染时间(ms)" width="120" />
<el-table-column
prop="memoryUsage"
label="内存占用(KB)"
width="130"
/>
<el-table-column prop="cpuUsage" label="CPU占用(%)" width="120" />
<el-table-column prop="rating" label="性能评级" width="120">
<template #default="{ row }">
<el-tag :type="getRatingType(row.rating)">{{
row.rating
}}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button size="small" @click="testSingleStyle(row.style)"
>单独测试</el-button
>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<!-- 最佳实践建议 -->
<el-row :gutter="20">
<el-col :span="12">
<el-card class="best-practices">
<template #header>
<span>💡 最佳实践建议</span>
</template>
<div class="practice-list">
<div
v-for="practice in bestPractices"
:key="practice.title"
class="practice-item"
>
<div class="practice-icon">{{ practice.icon }}</div>
<div class="practice-content">
<h4>{{ practice.title }}</h4>
<p>{{ practice.description }}</p>
<div class="practice-example">
<strong>推荐:</strong> {{ practice.recommended.join(', ') }}
</div>
</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="usage-scenarios">
<template #header>
<span>🎯 使用场景推荐</span>
</template>
<div class="scenario-list">
<div
v-for="scenario in usageScenarios"
:key="scenario.scene"
class="scenario-item"
>
<div class="scenario-header">
<span class="scenario-icon">{{ scenario.icon }}</span>
<span class="scenario-title">{{ scenario.scene }}</span>
</div>
<div class="scenario-styles">
<el-tag
v-for="style in scenario.styles"
:key="style"
class="style-tag"
@click="demoScenario(style)"
>
{{ style }}
</el-tag>
</div>
<p class="scenario-reason">{{ scenario.reason }}</p>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 实时性能监控 -->
<el-card class="performance-monitor">
<template #header>
<div class="monitor-header">
<span>📊 实时性能监控</span>
<el-switch v-model="monitoring" @change="toggleMonitoring" />
</div>
</template>
<div class="monitor-content">
<el-row :gutter="20">
<el-col :span="8">
<div class="monitor-item">
<div class="monitor-label">当前FPS</div>
<div class="monitor-value">{{ currentFPS }}</div>
<div class="monitor-chart">
<div
v-for="(fps, index) in fpsHistory"
:key="index"
class="fps-bar"
:style="{ height: `${(fps / 60) * 100}%` }"
/>
</div>
</div>
</el-col>
<el-col :span="8">
<div class="monitor-item">
<div class="monitor-label">内存使用</div>
<div class="monitor-value">{{ currentMemory }}MB</div>
<div class="monitor-progress">
<el-progress
:percentage="memoryPercentage"
:color="getMemoryColor"
/>
</div>
</div>
</el-col>
<el-col :span="8">
<div class="monitor-item">
<div class="monitor-label">活跃Loading</div>
<div class="monitor-value">{{ activeLoadings }}</div>
<div class="loading-list">
<el-tag
v-for="loading in activeLoadingList"
:key="loading"
size="small"
class="loading-tag"
>
{{ loading }}
</el-tag>
</div>
</div>
</el-col>
</el-row>
</div>
</el-card>
<!-- 压力测试 -->
<el-card class="stress-test">
<template #header>
<span>🔥 压力测试</span>
</template>
<div class="stress-controls">
<el-row :gutter="20">
<el-col :span="6">
<label>并发数量:</label>
<el-input-number v-model="stressConfig.count" :min="1" :max="100" />
</el-col>
<el-col :span="6">
<label>测试样式:</label>
<el-select v-model="stressConfig.style">
<el-option
v-for="style in allStyles"
:key="style"
:label="style"
:value="style"
/>
</el-select>
</el-col>
<el-col :span="6">
<label>持续时间(秒):</label>
<el-input-number
v-model="stressConfig.duration"
:min="1"
:max="60"
/>
</el-col>
<el-col :span="6">
<el-button
type="danger"
:loading="stressTesting"
@click="startStressTest"
>
开始压力测试
</el-button>
</el-col>
</el-row>
</div>
<div v-if="stressTesting" class="stress-grid">
<div v-for="n in stressConfig.count" :key="n" class="stress-item">
<el-card
v-loadingPro="{
showLoading: true,
animation: stressConfig.style,
}"
element-loading-text="压力测试"
class="stress-card"
>
<div class="stress-content">测试{{ n }}</div>
</el-card>
</div>
</div>
<div v-if="stressResults" class="stress-results">
<h4>压力测试结果:</h4>
<el-descriptions :column="2" border>
<el-descriptions-item label="测试样式">{{
stressResults.style
}}</el-descriptions-item>
<el-descriptions-item label="并发数量">
<el-text>{{ stressResults.count }}</el-text>
</el-descriptions-item>
<el-descriptions-item label="平均FPS">{{
stressResults.avgFPS
}}</el-descriptions-item>
<el-descriptions-item label="最低FPS">{{
stressResults.minFPS
}}</el-descriptions-item>
<el-descriptions-item label="内存峰值"
>{{ stressResults.peakMemory }}MB</el-descriptions-item
>
<el-descriptions-item label="性能评分"
>{{ stressResults.score }}/100</el-descriptions-item
>
</el-descriptions>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { ElLoadingPro, ElMessage } from 'element-plus'
// 所有样式
const allStyles = [
'spinner',
'pulse',
'dots',
'bounce',
'cube',
'gradient',
'heartbeat',
'spiral',
'blink',
'swing',
'breath',
'star',
'kitty',
'rainbow',
'love',
'duck',
'bee',
'bubble',
'rocket',
'candy',
'flower',
'butterfly',
'fish',
'sun',
'moon',
'cloud',
'lightning',
'snowflake',
'leaf',
'cherry',
]
// 性能测试数据
const testing = ref(false)
const performanceResults: any = ref([])
// 监控数据
const monitoring = ref(false)
const currentFPS: any = ref(60)
const currentMemory = ref(0)
const fpsHistory: any = ref(Array.from({ length: 20 }).fill(60))
const activeLoadings = ref(0)
const activeLoadingList = ref([])
// 压力测试
const stressTesting = ref(false)
const stressConfig = ref({
count: 10,
style: 'spinner',
duration: 10,
})
const stressResults: any = ref(null)
// 最佳实践数据
const bestPractices = [
{
icon: '🚀',
title: '轻量级场景',
description: '适用于快速响应的场景,如表单提交、简单查询等',
recommended: ['spinner', 'pulse', 'dots'],
},
{
icon: '🎨',
title: '用户体验优先',
description: '适用于需要提升用户体验的场景,如首页加载、重要操作等',
recommended: ['rainbow', 'love', 'kitty', 'butterfly'],
},
{
icon: '⚡',
title: '高性能要求',
description: '适用于性能敏感的场景,如大数据表格、实时更新等',
recommended: ['blink', 'breath', 'gradient'],
},
{
icon: '📱',
title: '移动端优化',
description: '适用于移动设备,考虑电池和性能限制',
recommended: ['pulse', 'dots', 'swing'],
},
]
// 使用场景推荐
const usageScenarios = [
{
icon: '💼',
scene: '企业应用',
styles: ['spinner', 'cube', 'gradient'],
reason: '专业、稳重,符合企业形象',
},
{
icon: '🎮',
scene: '游戏应用',
styles: ['rocket', 'star', 'lightning'],
reason: '动感、有趣,增强游戏体验',
},
{
icon: '🛒',
scene: '电商平台',
styles: ['love', 'rainbow', 'flower'],
reason: '温馨、友好,提升购物体验',
},
{
icon: '📚',
scene: '教育平台',
styles: ['kitty', 'bee', 'butterfly'],
reason: '可爱、生动,吸引学习兴趣',
},
{
icon: '🏥',
scene: '医疗健康',
styles: ['heartbeat', 'breath', 'pulse'],
reason: '专业、关怀,符合医疗主题',
},
{
icon: '🌱',
scene: '环保公益',
styles: ['leaf', 'flower', 'sun'],
reason: '自然、环保,传达绿色理念',
},
]
// 计算属性
const memoryPercentage = computed(() => {
return Math.min((currentMemory.value / 100) * 100, 100)
})
const getMemoryColor = computed(() => {
if (memoryPercentage.value < 50) return '#67c23a'
if (memoryPercentage.value < 80) return '#e6a23c'
return '#f56c6c'
})
// 性能监控定时器
let monitorTimer: any = null
let fpsCounter = 0
let lastTime = performance.now()
// 方法
const runPerformanceTest = async () => {
testing.value = true
performanceResults.value = []
for (const style of allStyles) {
const result: any = await testStylePerformance(style)
performanceResults.value.push(result)
}
testing.value = false
ElMessage.success('性能测试完成')
}
const testStylePerformance = async (style: string) => {
return new Promise((resolve) => {
const startTime = performance.now()
const startMemory = (performance as any).memory?.usedJSHeapSize || 0
// 模拟渲染测试
const testElement = document.createElement('div')
testElement.className = `loading-pro-${style}`
document.body.appendChild(testElement)
setTimeout(() => {
const endTime = performance.now()
const endMemory = (performance as any).memory?.usedJSHeapSize || 0
document.body.removeChild(testElement)
const renderTime = Math.round(endTime - startTime)
const memoryUsage = Math.round((endMemory - startMemory) / 1024)
const cpuUsage = Math.random() * 20 + 5 // 模拟CPU使用率
let rating = 'A'
if (renderTime > 50 || memoryUsage > 100) rating = 'B'
if (renderTime > 100 || memoryUsage > 200) rating = 'C'
if (renderTime > 200 || memoryUsage > 500) rating = 'D'
resolve({
style,
category: allStyles.indexOf(style) < 12 ? '基础' : '可爱',
renderTime,
memoryUsage: Math.max(memoryUsage, 10),
cpuUsage: Math.round(cpuUsage),
rating,
})
}, 100)
})
}
const testSingleStyle = async (style: string) => {
const loading = ElLoadingPro.service({
lock: true,
text: `测试 ${style} 性能...`,
animation: style,
})
const result: any = await testStylePerformance(style)
loading.close()
ElMessage.success(
`${style} 测试完成: 渲染${result.renderTime}ms, 内存${result.memoryUsage}KB`
)
}
const getRatingType = (rating: string) => {
const types = { A: 'success', B: 'warning', C: 'danger', D: 'info' }
return types[rating] || 'info'
}
const demoScenario = (style: string) => {
const loading = ElLoadingPro.service({
lock: true,
text: `${style} 场景演示`,
animation: style,
background: 'rgba(0, 0, 0, 0.8)',
})
setTimeout(() => {
loading.close()
}, 2000)
}
const toggleMonitoring = (enabled: any) => {
if (enabled) {
startMonitoring()
} else {
stopMonitoring()
}
}
const startMonitoring = () => {
monitorTimer = setInterval(() => {
// 更新FPS
const now = performance.now()
fpsCounter++
if (now - lastTime >= 1000) {
currentFPS.value = Math.round((fpsCounter * 1000) / (now - lastTime))
fpsHistory.value.shift()
fpsHistory.value.push(currentFPS.value)
fpsCounter = 0
lastTime = now
}
// 更新内存
if ((performance as any).memory) {
currentMemory.value = Math.round(
(performance as any).memory.usedJSHeapSize / 1024 / 1024
)
}
// 更新活跃loading数量
activeLoadings.value = document.querySelectorAll('.el-loading-mask').length
}, 100)
}
const stopMonitoring = () => {
if (monitorTimer) {
clearInterval(monitorTimer)
monitorTimer = null
}
}
const startStressTest = () => {
stressTesting.value = true
stressResults.value = null
const startTime = performance.now()
const startMemory = (performance as any).memory?.usedJSHeapSize || 0
const fpsData: any = []
const testTimer = setInterval(() => {
fpsData.push(currentFPS.value)
}, 100)
setTimeout(() => {
clearInterval(testTimer)
stressTesting.value = false
const endMemory = (performance as any).memory?.usedJSHeapSize || 0
const avgFPS = Math.round(
fpsData.reduce((a, b) => a + b, 0) / fpsData.length
)
const minFPS = Math.min(...fpsData)
const peakMemory = Math.round((endMemory - startMemory) / 1024 / 1024)
let score = 100
if (avgFPS < 30) score -= 30
if (minFPS < 15) score -= 20
if (peakMemory > 50) score -= 25
stressResults.value = {
style: stressConfig.value.style,
count: stressConfig.value.count,
avgFPS,
minFPS,
peakMemory: Math.max(peakMemory, 1),
score: Math.max(score, 0),
}
ElMessage.success('压力测试完成')
}, stressConfig.value.duration * 1000)
}
onMounted(() => {
// 初始化性能数据
if ((performance as any).memory) {
currentMemory.value = Math.round(
(performance as any).memory.usedJSHeapSize / 1024 / 1024
)
}
})
onUnmounted(() => {
stopMonitoring()
})
</script>
<style scoped>
.performance-showcase {
padding: 20px;
max-width: 1400px;
margin: 0 auto;
}
.showcase-header {
text-align: center;
margin-bottom: 30px;
}
.showcase-header h1 {
font-size: 28px;
color: #409eff;
margin-bottom: 10px;
}
.showcase-header p {
color: #666;
font-size: 16px;
}
.performance-test {
margin-bottom: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.test-results {
margin-top: 20px;
}
.best-practices,
.usage-scenarios {
margin-bottom: 20px;
height: 500px;
overflow-y: auto;
}
.practice-list,
.scenario-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.practice-item {
display: flex;
gap: 15px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #409eff;
}
.practice-icon {
font-size: 24px;
flex-shrink: 0;
}
.practice-content h4 {
margin: 0 0 8px 0;
color: #303133;
}
.practice-content p {
margin: 0 0 10px 0;
color: #666;
font-size: 14px;
}
.practice-example {
font-size: 13px;
color: #409eff;
}
.scenario-item {
padding: 15px;
border: 1px solid #e4e7ed;
border-radius: 8px;
background: white;
}
.scenario-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.scenario-icon {
font-size: 20px;
}
.scenario-title {
font-weight: 600;
color: #303133;
}
.scenario-styles {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 10px;
}
.style-tag {
cursor: pointer;
transition: all 0.3s;
}
.style-tag:hover {
transform: scale(1.05);
}
.scenario-reason {
font-size: 13px;
color: #666;
margin: 0;
}
.performance-monitor {
margin-bottom: 20px;
}
.monitor-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.monitor-content {
margin-top: 20px;
}
.monitor-item {
text-align: center;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
}
.monitor-label {
font-size: 14px;
color: #666;
margin-bottom: 10px;
}
.monitor-value {
font-size: 24px;
font-weight: bold;
color: #409eff;
margin-bottom: 15px;
}
.monitor-chart {
display: flex;
align-items: end;
justify-content: center;
gap: 2px;
height: 40px;
}
.fps-bar {
width: 3px;
background: linear-gradient(to top, #f56c6c, #e6a23c, #67c23a);
border-radius: 1px;
transition: height 0.3s;
}
.monitor-progress {
margin-top: 10px;
}
.loading-list {
display: flex;
flex-wrap: wrap;
gap: 5px;
justify-content: center;
margin-top: 10px;
}
.loading-tag {
font-size: 11px;
}
.stress-test {
margin-bottom: 20px;
}
.stress-controls {
margin-bottom: 20px;
}
.stress-controls label {
display: block;
margin-bottom: 5px;
font-size: 14px;
color: #303133;
}
.stress-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 10px;
margin-bottom: 20px;
}
.stress-item {
height: 100px;
}
.stress-card {
height: 100%;
}
.stress-content {
display: flex;
align-items: center;
justify-content: center;
height: 60px;
font-size: 12px;
color: #666;
}
.stress-results {
margin-top: 20px;
padding: 20px;
border-radius: 8px;
}
.stress-results h4 {
margin: 0 0 15px 0;
color: #303133;
}
/* 响应式设计 */
@media (max-width: 768px) {
.stress-grid {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.monitor-chart {
height: 30px;
}
.monitor-value {
font-size: 18px;
}
}
</style>
Loading Pro 样式画廊
欢迎来到果锅的 Loading Pro 样式画廊!这里是一个精心策划的视觉空间,专门展示我们 Loading Pro 组件所能呈现的丰富多样、引人入胜的加载效果。我们深知,加载状态不仅仅是技术上的等待,更是用户体验中不可或缺的一环,它直接影响着用户对产品流畅度和专业度的感知。Show a full screen animation while loading data.
🎨 Loading Pro 样式画廊
35种精美loading动画效果完整展示
<!-- @format -->
<template>
<div class="style-gallery">
<div class="gallery-header">
<h1>🎨 Loading Pro 样式画廊</h1>
<p>35种精美loading动画效果完整展示</p>
</div>
<!-- 搜索和筛选 -->
<div class="filter-section">
<el-input
v-model="searchText"
placeholder="搜索样式名称..."
prefix-icon="Search"
class="search-input"
clearable
/>
<el-select
v-model="selectedCategory"
placeholder="选择分类"
class="category-select"
>
<el-option label="全部" value="all" />
<el-option label="基础样式" value="basic" />
<el-option label="可爱样式" value="cute" />
</el-select>
</div>
<!-- 样式展示网格 -->
<div class="styles-grid">
<div
v-for="style in filteredStyles"
:key="style.name"
class="style-card"
@click="showStyleDemo(style.name)"
>
<div class="card-header">
<span class="style-number">{{ style.id }}</span>
<span class="style-category">{{ style.category }}</span>
</div>
<div class="loading-preview">
<el-card
v-loadingPro="{
showLoading: true,
animation: style.name,
}"
:element-loading-animation="style.name"
class="preview-card"
>
<div class="preview-content">
<div class="style-icon">{{ style.icon }}</div>
<div class="style-title">{{ style.name }}</div>
</div>
</el-card>
</div>
<div class="card-footer">
<div class="style-description">{{ style.description }}</div>
<div class="action-buttons">
<el-button
size="small"
type="primary"
@click.stop="copyCode(style.name)"
>
📋 复制代码
</el-button>
<el-button
size="small"
type="success"
@click.stop="showFullscreen(style.name)"
>
🖥️ 全屏
</el-button>
</div>
</div>
</div>
</div>
<!-- 代码示例弹窗 -->
<el-dialog v-model="codeDialogVisible" title="代码示例" width="600px">
<div class="code-example">
<h4>指令方式使用:</h4>
<pre><code><el-table
v-loadingPro="loading"
element-loading-animation="{{ currentStyle }}"
element-loading-text="Loading..."
:data="tableData"
>
<!-- 表格内容 -->
</el-table></code></pre>
<h4>服务方式使用:</h4>
<pre><code>import { ElLoadingPro } from 'element-plus'
const loading = ElLoadingPro.service({
lock: true,
text: 'Loading...',
animation: '{{ currentStyle }}',
background: 'rgba(0, 0, 0, 0.7)'
})
// 关闭loading
setTimeout(() => {
loading.close()
}, 2000)</code></pre>
</div>
<template #footer>
<el-button @click="codeDialogVisible = false">关闭</el-button>
<el-button type="primary" @click="copyAllCode">复制全部代码</el-button>
</template>
</el-dialog>
<!-- 统计信息 -->
<div class="stats-section">
<el-row :gutter="20">
<el-col :span="8">
<el-statistic
title="总样式数"
:value="allStylesData.length"
suffix="种"
/>
</el-col>
<el-col :span="8">
<el-statistic
title="基础样式"
:value="basicStylesCount"
suffix="种"
/>
</el-col>
<el-col :span="8">
<el-statistic title="可爱样式" :value="cuteStylesCount" suffix="种" />
</el-col>
</el-row>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { ElLoadingPro, ElMessage } from 'element-plus'
// 样式数据
const allStylesData = [
// 基础样式 (1-17)
{
id: 1,
name: 'spinner',
category: '基础',
icon: '🌀',
description: '经典旋转圆环效果',
},
{
id: 2,
name: 'pulse',
category: '基础',
icon: '💓',
description: '脉冲缩放效果',
},
{
id: 3,
name: 'dots',
category: '基础',
icon: '⚫',
description: '点阵波浪效果',
},
{
id: 4,
name: 'bounce',
category: '基础',
icon: '⚽',
description: '弹跳球效果',
},
{
id: 5,
name: 'cube',
category: '基础',
icon: '🎲',
description: '3D立方体旋转',
},
{
id: 6,
name: 'gradient',
category: '基础',
icon: '🌈',
description: '渐变圆环效果',
},
{
id: 7,
name: 'heartbeat',
category: '基础',
icon: '💗',
description: '心跳节拍效果',
},
{
id: 8,
name: 'spiral',
category: '基础',
icon: '🌪️',
description: '螺旋旋转效果',
},
{
id: 9,
name: 'blink',
category: '基础',
icon: '✨',
description: '闪烁灯光效果',
},
{
id: 10,
name: 'swing',
category: '基础',
icon: '🎯',
description: '钟摆摆动效果',
},
{
id: 11,
name: 'breath',
category: '基础',
icon: '🫁',
description: '呼吸伸缩效果',
},
{
id: 12,
name: 'star',
category: '基础',
icon: '⭐',
description: '星形旋转效果',
},
// 可爱样式 (18-35)
{
id: 18,
name: 'kitty',
category: '可爱',
icon: '🐱',
description: '小猫咪摆尾巴',
},
{
id: 19,
name: 'rainbow',
category: '可爱',
icon: '🌈',
description: '彩虹圈旋转',
},
{
id: 20,
name: 'love',
category: '可爱',
icon: '💖',
description: '爱心跳动效果',
},
{
id: 21,
name: 'duck',
category: '可爱',
icon: '🦆',
description: '小鸭子游泳',
},
{
id: 22,
name: 'bee',
category: '可爱',
icon: '🐝',
description: '小蜜蜂飞舞',
},
{
id: 23,
name: 'bubble',
category: '可爱',
icon: '🫧',
description: '泡泡浮动效果',
},
{
id: 24,
name: 'rocket',
category: '可爱',
icon: '🚀',
description: '小火箭发射',
},
{
id: 25,
name: 'candy',
category: '可爱',
icon: '🍭',
description: '跳跳糖效果',
},
{
id: 26,
name: 'flower',
category: '可爱',
icon: '🌸',
description: '小花朵绽放',
},
{
id: 27,
name: 'butterfly',
category: '可爱',
icon: '🦋',
description: '蝴蝶飞舞',
},
{
id: 28,
name: 'fish',
category: '可爱',
icon: '🐠',
description: '小鱼游泳',
},
{
id: 29,
name: 'sun',
category: '可爱',
icon: '☀️',
description: '太阳光芒',
},
{
id: 30,
name: 'moon',
category: '可爱',
icon: '🌙',
description: '月亮相位',
},
{
id: 31,
name: 'cloud',
category: '可爱',
icon: '☁️',
description: '云朵飘动',
},
{
id: 32,
name: 'lightning',
category: '可爱',
icon: '⚡',
description: '闪电效果',
},
{
id: 33,
name: 'snowflake',
category: '可爱',
icon: '❄️',
description: '雪花飘落',
},
{
id: 34,
name: 'leaf',
category: '可爱',
icon: '🍃',
description: '叶子飞舞',
},
{
id: 35,
name: 'cherry',
category: '可爱',
icon: '🌸',
description: '樱花飘落',
},
]
// 响应式数据
const searchText = ref('')
const selectedCategory = ref('all')
const codeDialogVisible = ref(false)
const currentStyle = ref('')
// 计算属性
const filteredStyles = computed(() => {
let filtered = allStylesData
// 按分类筛选
if (selectedCategory.value !== 'all') {
filtered = filtered.filter((style) => {
if (selectedCategory.value === 'basic') {
return style.category === '基础'
} else if (selectedCategory.value === 'cute') {
return style.category === '可爱'
}
return true
})
}
// 按搜索文本筛选
if (searchText.value) {
filtered = filtered.filter(
(style) =>
style.name.toLowerCase().includes(searchText.value.toLowerCase()) ||
style.description.includes(searchText.value)
)
}
return filtered
})
const basicStylesCount = computed(
() => allStylesData.filter((style) => style.category === '基础').length
)
const cuteStylesCount = computed(
() => allStylesData.filter((style) => style.category === '可爱').length
)
// 方法
const showStyleDemo = (styleName: string) => {
const loading = ElLoadingPro.service({
lock: true,
text: `${styleName} 样式演示`,
animation: styleName,
background: 'rgba(0, 0, 0, 0.8)',
})
setTimeout(() => {
loading.close()
}, 3000)
}
const copyCode = (styleName: string) => {
currentStyle.value = styleName
codeDialogVisible.value = true
}
const copyAllCode = () => {
const code = `// 指令方式
<el-table
v-loadingPro="loading"
element-loading-animation="${currentStyle.value}"
element-loading-text="Loading..."
:data="tableData"
>
<!-- 表格内容 -->
</el-table>
// 服务方式
import { ElLoadingPro } from 'element-plus'
const loading = ElLoadingPro.service({
lock: true,
text: 'Loading...',
animation: '${currentStyle.value}',
background: 'rgba(0, 0, 0, 0.7)'
})
setTimeout(() => {
loading.close()
}, 2000)`
navigator.clipboard.writeText(code).then(() => {
ElMessage.success('代码已复制到剪贴板')
codeDialogVisible.value = false
})
}
const showFullscreen = (styleName: string) => {
const loading = ElLoadingPro.service({
lock: true,
text: `🎉 ${styleName} 全屏展示`,
animation: styleName,
background: 'rgba(0, 0, 0, 0.9)',
})
setTimeout(() => {
loading.close()
}, 4000)
}
</script>
<style scoped>
.style-gallery {
padding: 20px;
max-width: 1400px;
margin: 0 auto;
}
.gallery-header {
text-align: center;
margin-bottom: 30px;
}
.gallery-header h1 {
font-size: 32px;
color: #409eff;
margin-bottom: 10px;
}
.gallery-header p {
font-size: 16px;
color: #666;
}
.filter-section {
display: flex;
gap: 20px;
margin-bottom: 30px;
justify-content: center;
align-items: center;
}
.search-input {
width: 300px;
}
.category-select {
width: 150px;
}
.styles-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.style-card {
border: 1px solid #e4e7ed;
border-radius: 12px;
overflow: hidden;
transition: all 0.3s ease;
cursor: pointer;
background: white;
}
.style-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
border-color: #409eff;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.style-number {
font-weight: bold;
color: #409eff;
font-size: 14px;
}
.style-category {
font-size: 12px;
padding: 2px 8px;
border-radius: 12px;
background: #409eff;
color: white;
}
.loading-preview {
height: 150px;
display: flex;
align-items: center;
justify-content: center;
background: #fafafa;
}
.preview-card {
width: 100%;
height: 100%;
border: none;
box-shadow: none;
}
.preview-content {
text-align: center;
}
.style-icon {
font-size: 24px;
margin-bottom: 8px;
}
.style-title {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.card-footer {
padding: 16px;
}
.style-description {
font-size: 13px;
color: #666;
margin-bottom: 12px;
text-align: center;
}
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
}
.code-example {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
}
.code-example h4 {
margin: 0 0 10px 0;
color: #303133;
}
.code-example pre {
background: #2d3748;
color: #e2e8f0;
padding: 15px;
border-radius: 6px;
overflow-x: auto;
margin: 10px 0;
font-size: 13px;
line-height: 1.5;
}
.stats-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 30px;
border-radius: 12px;
color: white;
}
:deep(.el-statistic__content) {
color: white !important;
}
:deep(.el-statistic__number) {
color: white !important;
}
:deep(.el-statistic__suffix) {
color: white !important;
}
/* 响应式设计 */
@media (max-width: 768px) {
.styles-grid {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 15px;
}
.filter-section {
flex-direction: column;
gap: 15px;
}
.search-input {
width: 100%;
}
.action-buttons {
flex-direction: column;
}
}
/* loading遮罩层样式 */
:deep(.el-loading-mask) {
border-radius: 8px;
}
:deep(.el-loading-spinner) {
margin-top: -25px;
}
:deep(.el-loading-text) {
margin-top: 15px;
font-size: 13px;
}
</style>
Service
You can also invoke Loading with a service. Import Loading service:
import { ElLoading } from 'element-plus';
Invoke it:
ElLoading.service(options);
The parameter options
is the configuration of Loading, and its details can be found in the following table. LoadingService
returns a Loading instance, and you can close it by invoking its close
method:
const loadingInstance = ElLoading.service(options);
nextTick(() => {
// Loading should be closed asynchronously
loadingInstance.close();
});
Note that in this case the full screen Loading is singleton. If a new full screen Loading is invoked before an existing one is closed, the existing full screen Loading instance will be returned instead of actually creating another Loading instance:
const loadingInstance1 = ElLoading.service({ fullscreen: true });
const loadingInstance2 = ElLoading.service({ fullscreen: true });
console.log(loadingInstance1 === loadingInstance2); // true
Calling the close
method on any one of them can close this full screen Loading.
If Element Plus is imported entirely, a globally method $loading
will be registered to app.config.globalProperties
. You can invoke it like this: this.$loading(options)
, and it also returns a Loading instance.
API
Options
Name | Description | Type | Default |
---|---|---|---|
target | the DOM node Loading needs to cover. Accepts a DOM object or a string. If it's a string, it will be passed todocument.querySelector to get the corresponding DOM node | string / HTMLElement | document.body |
body | same as thebody modifier of v-loading | boolean | false |
fullscreen | same as thefullscreen modifier of v-loading | boolean | true |
lock | same as thelock modifier of v-loading | boolean | false |
text | loading text that displays under the spinner | string | — |
spinner | class name of the custom spinner | string | — |
background | background color of the mask | string | — |
customClass | custom class name for loading | string | — |
svg | custom SVG element to override the default loading spinner | string | — |
svgViewBox | sets the viewBox attribute for loading svg element | string | — |
beforeClose 2.7.8 | Function executed before loading attempts to close. If this function returns false, the closing process will be aborted. Otherwise, the loading will close. | Function | — |
closed 2.7.8 | Function triggered after loading has completely closed | Function | — |
Directives
Name | Description | Type |
---|---|---|
v-loading | 具体内容见下方新增 | Object |
New Props
Name | Description | Type |
---|---|---|
showLoading | 是否显示 loading [仅自定义指令有效] | Boolean |
animation | 动画 | 见下方枚举类型 Animation Enum |
backgroundColor | 会影响动画颜色 | String |
borderColor | 会影响动画颜色 | String |
color | 字体颜色 | String |
width | 内部动画的大小 | String |
hegit | 内部动画的大小 | String |
Animation Enum
序号 | 名称 | 动画名称 |
---|---|---|
1 | 弧线效果 | spinner |
2 | 脉冲效果 | pulse |
3 | 点阵效果 | dots |
4 | 弹跳球效果 | bounce |
5 | 旋转方块效果 | cube |
6 | 渐变环效果 | gradient |
7 | 心跳效果 | heartbeat |
8 | 螺旋效果 | spiral |
9 | 闪烁效果 | blink |
10 | 摆动效果 | swing |
15 | 呼吸效果 | breath |
17 | 星形效果 | star |
18 | 小猫咪效果 | kitty |
19 | 彩虹圈效果 | rainbow |
20 | 爱心跳动效果 | love |
21 | 小鸭子游泳效果 | duck |
22 | 小蜜蜂飞舞效果 | bee |
23 | 泡泡效果 | bubble |
24 | 小火箭效果 | rocket |
25 | 跳跳糖效果 | candy |
26 | 小花朵效果 | flower |
27 | 蝴蝶飞舞效果 | butterfly |
28 | 小鱼游泳效果 | fish |
29 | 太阳效果 | sun |
30 | 月亮效果 | moon |
31 | 云朵效果 | cloud |
32 | 闪电效果 | lightning |
33 | 雪花效果 | snowflake |
34 | 叶子效果 | leaf |
35 | 樱花效果 | cherry |