1. 旋转圆圈加载动画
<template>
<div class="loading-spinner"></div>
</template>
<style>
.loading-spinner {
display: inline-block;
width: 40px;
height: 40px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: #3498db;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
2. 点状加载动画
<template>
<div class="dot-loading">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</template>
<style>
.dot-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
height: 40px;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #3498db;
animation: bounce 1.4s infinite ease-in-out;
}
.dot:nth-child(1) {
animation-delay: -0.32s;
}
.dot:nth-child(2) {
animation-delay: -0.16s;
}
@keyframes bounce {
0%, 80%, 100% {
transform: scale(0);
} 40% {
transform: scale(1);
}
}
</style>
3. 条形加载动画
<template>
<div class="bar-loading">
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</div>
</template>
<style>
.bar-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 3px;
height: 40px;
}
.bar {
width: 6px;
height: 25px;
background-color: #3498db;
animation: stretch 1.2s infinite ease-in-out;
}
.bar:nth-child(1) {
animation-delay: -1.2s;
}
.bar:nth-child(2) {
animation-delay: -1.1s;
}
.bar:nth-child(3) {
animation-delay: -1.0s;
}
.bar:nth-child(4) {
animation-delay: -0.9s;
}
.bar:nth-child(5) {
animation-delay: -0.8s;
}
@keyframes stretch {
0%, 40%, 100% {
transform: scaleY(0.4);
} 20% {
transform: scaleY(1);
}
}
</style>
你可以将这些加载动画组件化,例如:
<template>
<div>
<h1>我的应用</h1>
<LoadingSpinner v-if="isLoading" />
<div v-else>
<!-- 你的内容 -->
</div>
</div>
</template>
<script>
import LoadingSpinner from './LoadingSpinner.vue';
export default {
components: {
LoadingSpinner
},
data() {
return {
isLoading: true
}
},
mounted() {
// 模拟数据加载
setTimeout(() => {
this.isLoading = false;
}, 2000);
}
}
</script>
<template>
<div class="simple-markdown" v-html="renderMarkdown(markdownText)"></div>
</template>
<script>
export default {
data() {
return {
markdownText: '# Hello World\n\nThis is **Markdown** content.'
}
},
methods: {
renderMarkdown(text) {
// 简单的替换规则
return text
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
.replace(/\*\*(.*)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*)\*/g, '<em>$1</em>')
.replace(/\[(.*)\]\((.*)\)/g, '<a href="$2">$1</a>')
.replace(/\n/g, '<br>');
}
}
}
.typing-effect {
border-right: 2px solid #000; /* 光标效果 */
white-space: pre-wrap; /* 保留空格和换行 */
overflow: hidden;
animation: blink 0.7s infinite; /* 光标闪烁动画 */
}
@keyframes blink {
from { border-right-color: transparent; }
to { border-right-color: #000; }
}
startLoop() {
let count = 0;
const max = 10;
const loop = () => {
if (count >= max) return;
this.message = `Count: ${count}`; // 更新响应式数据
count++;
setTimeout(loop, 300); // 控制速度(300ms/次)
};
loop();
}
export default {
data() {
return {
frameCount: 0,
animationId: null,
lastTime: 0,
targetFPS: 60, // 目标帧率
}
},
methods: {
startAnimation() {
this.lastTime = performance.now();
this.animationLoop();
},
animationLoop(timestamp) {
// 计算时间差
const delta = timestamp - this.lastTime;
const interval = 1000 / this.targetFPS;
if (delta >= interval) {
// ✅ 关键帧更新
this.frameCount++;
this.lastTime = timestamp - (delta % interval); // 保持时间同步
}
this.animationId = requestAnimationFrame(this.animationLoop);
},
stopAnimation() {
cancelAnimationFrame(this.animationId);
}
},
mounted() {
this.startAnimation();
},
beforeDestroy() {
this.stopAnimation(); // 必须清理!
}
}
</script>
222222222222222222222
于 2025-05-22 09:20:37 首次发布