mirror of
https://gitcode.com/leisileken/cloud-smart.git
synced 2026-09-11 10:27:20 +08:00
feat: 恢复深色主题版本,替换星空背景视频
This commit is contained in:
372
src/components/CosmicBackground.vue
Normal file
372
src/components/CosmicBackground.vue
Normal file
@@ -0,0 +1,372 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
interface Particle {
|
||||
element: HTMLElement
|
||||
angle: number
|
||||
radius: number
|
||||
speed: number
|
||||
size: number
|
||||
opacity: number
|
||||
baseOpacity: number
|
||||
color: string
|
||||
centerX: number
|
||||
centerY: number
|
||||
twinkleSpeed: number
|
||||
twinkleOffset: number
|
||||
}
|
||||
|
||||
const particlesContainer = ref<HTMLDivElement | null>(null)
|
||||
const particles: Particle[] = []
|
||||
const mousePos = { x: 0, y: 0 }
|
||||
let animationFrameId: number | null = null
|
||||
let time = 0
|
||||
|
||||
const nebulaColors = [
|
||||
'#6366f1',
|
||||
'#8b5cf6',
|
||||
'#a855f7',
|
||||
'#d946ef',
|
||||
'#ec4899',
|
||||
'#f43f5e',
|
||||
'#3b82f6',
|
||||
'#60a5fa',
|
||||
'#22d3ee',
|
||||
'#14b8a6',
|
||||
]
|
||||
|
||||
const createNebulaParticle = (centerX: number, centerY: number, maxRadius: number) => {
|
||||
const particle = document.createElement('div')
|
||||
particle.className = 'particle nebula'
|
||||
particle.style.position = 'absolute'
|
||||
particle.style.borderRadius = '50%'
|
||||
particle.style.pointerEvents = 'none'
|
||||
particle.style.transition = 'opacity 0.3s ease'
|
||||
|
||||
const angle = Math.random() * Math.PI * 2
|
||||
const radius = Math.random() * maxRadius
|
||||
const size = Math.random() * 3 + 1
|
||||
const color = nebulaColors[Math.floor(Math.random() * nebulaColors.length)]
|
||||
const baseOpacity = Math.random() * 0.4 + 0.2
|
||||
|
||||
particle.style.width = `${size}px`
|
||||
particle.style.height = `${size}px`
|
||||
particle.style.backgroundColor = color
|
||||
particle.style.left = `${centerX + Math.cos(angle) * radius}px`
|
||||
particle.style.top = `${centerY + Math.sin(angle) * radius}px`
|
||||
particle.style.opacity = `${baseOpacity}`
|
||||
|
||||
if (particlesContainer.value) {
|
||||
particlesContainer.value.appendChild(particle)
|
||||
}
|
||||
|
||||
particles.push({
|
||||
element: particle,
|
||||
angle,
|
||||
radius,
|
||||
speed: (Math.random() * 0.001 + 0.0005) * (Math.random() > 0.5 ? 1 : -1),
|
||||
size,
|
||||
opacity: baseOpacity,
|
||||
baseOpacity,
|
||||
color,
|
||||
centerX,
|
||||
centerY,
|
||||
twinkleSpeed: Math.random() * 0.005 + 0.002,
|
||||
twinkleOffset: Math.random() * Math.PI * 2,
|
||||
})
|
||||
}
|
||||
|
||||
const createNebulaCenter = () => {
|
||||
const centerX = window.innerWidth / 2
|
||||
const centerY = window.innerHeight / 2
|
||||
|
||||
for (let i = 0; i < 150; i++) {
|
||||
createNebulaParticle(centerX, centerY, Math.min(window.innerWidth, window.innerHeight) * 0.4)
|
||||
}
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
createNebulaParticle(centerX, centerY, Math.min(window.innerWidth, window.innerHeight) * 0.6)
|
||||
}
|
||||
|
||||
for (let i = 0; i < 50; i++) {
|
||||
createNebulaParticle(centerX, centerY, Math.min(window.innerWidth, window.innerHeight) * 0.8)
|
||||
}
|
||||
}
|
||||
|
||||
const updateParticles = () => {
|
||||
particles.forEach((particle) => {
|
||||
const x = particle.centerX + Math.cos(particle.angle) * particle.radius
|
||||
const y = particle.centerY + Math.sin(particle.angle) * particle.radius
|
||||
|
||||
particle.element.style.left = `${x}px`
|
||||
particle.element.style.top = `${y}px`
|
||||
|
||||
particle.element.style.opacity = `${particle.baseOpacity}`
|
||||
|
||||
const glowSize = particle.size * 6
|
||||
particle.element.style.boxShadow = `0 0 ${glowSize}px ${particle.color}40`
|
||||
})
|
||||
|
||||
animationFrameId = requestAnimationFrame(updateParticles)
|
||||
}
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
mousePos.x = e.clientX
|
||||
mousePos.y = e.clientY
|
||||
}
|
||||
|
||||
const handleResize = () => {
|
||||
particles.forEach(p => {
|
||||
p.element.remove()
|
||||
})
|
||||
particles.length = 0
|
||||
createNebulaCenter()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
createNebulaCenter()
|
||||
updateParticles()
|
||||
window.addEventListener('mousemove', handleMouseMove)
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId)
|
||||
}
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
window.removeEventListener('resize', handleResize)
|
||||
particles.forEach(p => {
|
||||
p.element.remove()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="cosmic-background">
|
||||
<div ref="particlesContainer" class="particles-container"></div>
|
||||
<div class="bg-gradient-radial-from-center"></div>
|
||||
<div class="bg-gradient-radial-abyss"></div>
|
||||
<div class="galaxy-core"></div>
|
||||
<div class="galaxy-arms"></div>
|
||||
<div class="cosmic-dust"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.cosmic-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cosmic-background::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('/images/星云.png');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0.6;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.particles-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.bg-gradient-radial-from-center {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(30, 30, 60, 0.4) 0%,
|
||||
rgba(20, 20, 40, 0.3) 30%,
|
||||
rgba(10, 10, 20, 0.2) 60%,
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.bg-gradient-radial-abyss {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(
|
||||
ellipse 80% 50% at 50% 100%,
|
||||
rgba(0, 0, 0, 0.85) 0%,
|
||||
rgba(5, 5, 15, 0.7) 40%,
|
||||
rgba(10, 10, 30, 0.5) 70%,
|
||||
rgba(0, 0, 0, 0.8) 100%
|
||||
);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.galaxy-core {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.galaxy-arms {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
transform: translate(-50%, -50%);
|
||||
animation: rotate-arms 120s linear infinite;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.galaxy-arms::before,
|
||||
.galaxy-arms::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.galaxy-arms::before {
|
||||
background: conic-gradient(
|
||||
from 0deg,
|
||||
transparent 0deg,
|
||||
rgba(100, 100, 200, 0.02) 10deg,
|
||||
rgba(150, 150, 255, 0.04) 20deg,
|
||||
rgba(100, 100, 200, 0.02) 30deg,
|
||||
transparent 40deg,
|
||||
transparent 90deg,
|
||||
rgba(100, 100, 200, 0.02) 100deg,
|
||||
rgba(150, 150, 255, 0.04) 110deg,
|
||||
rgba(100, 100, 200, 0.02) 120deg,
|
||||
transparent 130deg,
|
||||
transparent 180deg,
|
||||
rgba(100, 100, 200, 0.02) 190deg,
|
||||
rgba(150, 150, 255, 0.04) 200deg,
|
||||
rgba(100, 100, 200, 0.02) 210deg,
|
||||
transparent 220deg,
|
||||
transparent 270deg,
|
||||
rgba(100, 100, 200, 0.02) 280deg,
|
||||
rgba(150, 150, 255, 0.04) 290deg,
|
||||
rgba(100, 100, 200, 0.02) 300deg,
|
||||
transparent 310deg,
|
||||
transparent 360deg
|
||||
);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.galaxy-arms::after {
|
||||
background: conic-gradient(
|
||||
from 45deg,
|
||||
transparent 0deg,
|
||||
rgba(180, 180, 255, 0.015) 15deg,
|
||||
rgba(200, 200, 255, 0.03) 30deg,
|
||||
rgba(180, 180, 255, 0.015) 45deg,
|
||||
transparent 60deg,
|
||||
transparent 150deg,
|
||||
rgba(180, 180, 255, 0.015) 165deg,
|
||||
rgba(200, 200, 255, 0.03) 180deg,
|
||||
rgba(180, 180, 255, 0.015) 195deg,
|
||||
transparent 210deg,
|
||||
transparent 300deg,
|
||||
rgba(180, 180, 255, 0.015) 315deg,
|
||||
rgba(200, 200, 255, 0.03) 330deg,
|
||||
rgba(180, 180, 255, 0.015) 345deg,
|
||||
transparent 360deg
|
||||
);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.cosmic-dust {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
radial-gradient(1px 1px at 20px 30px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(1px 1px at 40px 70px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(2px 2px at 90px 40px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 160px 120px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(2px 2px at 230px 80px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(1px 1px at 300px 150px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(1px 1px at 370px 60px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(2px 2px at 450px 200px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 520px 100px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(1px 1px at 600px 180px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(2px 2px at 680px 90px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 750px 250px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(1px 1px at 830px 130px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(2px 2px at 900px 300px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 980px 160px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(1px 1px at 20px 280px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(2px 2px at 100px 350px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 180px 420px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(1px 1px at 260px 300px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(2px 2px at 340px 480px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 420px 360px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(1px 1px at 500px 440px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(2px 2px at 580px 320px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 660px 500px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(1px 1px at 740px 380px, rgba(255,255,255,0.3), transparent),
|
||||
radial-gradient(2px 2px at 820px 560px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 900px 450px, rgba(255,255,255,0.2), transparent),
|
||||
radial-gradient(1px 1px at 980px 520px, rgba(255,255,255,0.3), transparent);
|
||||
background-size: 1000px 600px;
|
||||
animation: float-dust 60s linear infinite;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
@keyframes rotate-core {
|
||||
from {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate-arms {
|
||||
from {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-core {
|
||||
0%, 100% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float-dust {
|
||||
from {
|
||||
background-position: 0 0;
|
||||
}
|
||||
to {
|
||||
background-position: 1000px 600px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user