数据结构优化
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
<header class="fixed top-0 w-full z-50 glass-nav transition-all duration-300" id="main-header">
|
||||
<div class="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between">
|
||||
<div class="cursor-pointer group" @click="goTo('/')">
|
||||
<span class="font-serif text-2xl italic tracking-wider text-white group-hover:text-art-accent transition-colors">{{ siteTitle || '年糕崽崽.Dev' }}</span>
|
||||
<span class="font-serif text-2xl italic tracking-wider text-white group-hover:text-art-accent transition-colors">{{ siteTitle || '年糕崽崽' }}</span>
|
||||
<span class="font-serif text-2xl italic tracking-wider text-art-accent group-hover:text-white transition-colors">.Blog</span>
|
||||
</div>
|
||||
<nav class="hidden md:flex items-center gap-10">
|
||||
<button
|
||||
|
||||
@@ -9,29 +9,18 @@ const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||
|
||||
// --- 配置参数 ---
|
||||
const config = {
|
||||
starCount: 600, // 大幅增加星星数量
|
||||
rotationSpeed: 0.0002, // 减慢旋转速度,更具呼吸感
|
||||
clickMeteorChance: 1.0 // 点击触发流星概率
|
||||
starCount: 150, // 恢复到经典数量,避免过于密集
|
||||
meteorChance: 0.003, // 自然流星概率
|
||||
}
|
||||
|
||||
// 类型定义
|
||||
interface Star {
|
||||
x: number
|
||||
y: number
|
||||
|
||||
// 轨道参数
|
||||
orbitRadius: number
|
||||
angle: number
|
||||
angularSpeed: number
|
||||
|
||||
// 属性参数
|
||||
radius: number // 显示半径
|
||||
|
||||
// 闪烁参数
|
||||
alpha: number // 当前透明度
|
||||
baseAlpha: number // 基础透明度
|
||||
twinkleSpeed: number
|
||||
twinklePhase: number
|
||||
radius: number
|
||||
alpha: number
|
||||
fadingOut: boolean
|
||||
speed: number // 闪烁速度
|
||||
}
|
||||
|
||||
interface Meteor {
|
||||
@@ -68,8 +57,6 @@ let ctx: CanvasRenderingContext2D | null = null
|
||||
let animationFrameId: number
|
||||
let width = 0
|
||||
let height = 0
|
||||
let centerX = 0
|
||||
let centerY = 0
|
||||
|
||||
// 对象池
|
||||
const stars: Star[] = []
|
||||
@@ -77,49 +64,17 @@ const meteors: Meteor[] = []
|
||||
const waves: Wave[] = []
|
||||
const particles: Particle[] = []
|
||||
|
||||
// 初始化星星 (星系分布 + 大小差异化)
|
||||
// 初始化星星 (经典随机分布)
|
||||
const initStars = () => {
|
||||
stars.length = 0
|
||||
const maxRadius = Math.sqrt(width * width + height * height) * 0.8
|
||||
|
||||
for (let i = 0; i < config.starCount; i++) {
|
||||
// 随机轨道半径
|
||||
const orbitRadius = Math.random() * maxRadius
|
||||
const angle = Math.random() * Math.PI * 2
|
||||
|
||||
// 模拟不同大小的星星分布
|
||||
// 80% 是微小的背景星,15% 是中等星星,5% 是亮眼的大星
|
||||
const sizeRandom = Math.random()
|
||||
let radius, baseAlpha
|
||||
|
||||
if (sizeRandom > 0.95) {
|
||||
// 大星 (Hero Stars)
|
||||
radius = Math.random() * 1.5 + 1.2
|
||||
baseAlpha = Math.random() * 0.4 + 0.6 // 比较亮
|
||||
} else if (sizeRandom > 0.8) {
|
||||
// 中星
|
||||
radius = Math.random() * 0.8 + 0.6
|
||||
baseAlpha = Math.random() * 0.3 + 0.3
|
||||
} else {
|
||||
// 微星 (Background Dust)
|
||||
radius = Math.random() * 0.4 + 0.2
|
||||
baseAlpha = Math.random() * 0.3 + 0.1
|
||||
}
|
||||
|
||||
// 旋转速度微调:外圈通常比内圈慢(或快,取决于想要的视觉效果),这里给一点随机性
|
||||
const speedVariation = (Math.random() - 0.5) * 0.0001
|
||||
const angularSpeed = config.rotationSpeed + speedVariation
|
||||
|
||||
stars.push({
|
||||
x: 0, y: 0,
|
||||
orbitRadius,
|
||||
angle,
|
||||
angularSpeed,
|
||||
radius,
|
||||
alpha: baseAlpha,
|
||||
baseAlpha,
|
||||
twinkleSpeed: Math.random() * 0.02 + 0.005,
|
||||
twinklePhase: Math.random() * Math.PI * 2
|
||||
x: Math.random() * width,
|
||||
y: Math.random() * height,
|
||||
radius: Math.random() * 1.5,
|
||||
alpha: Math.random(),
|
||||
fadingOut: Math.random() > 0.5,
|
||||
speed: Math.random() * 0.02 + 0.005
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -131,49 +86,37 @@ const render = () => {
|
||||
// 1. 清空画布
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
|
||||
// 2. 绘制星星
|
||||
// 2. 绘制星星 (经典呼吸闪烁)
|
||||
ctx.fillStyle = '#FFF'
|
||||
stars.forEach(star => {
|
||||
// A. 更新角度 (纯自转,不再跟随鼠标)
|
||||
star.angle += star.angularSpeed
|
||||
|
||||
// B. 计算位置
|
||||
star.x = centerX + Math.cos(star.angle) * star.orbitRadius
|
||||
star.y = centerY + Math.sin(star.angle) * star.orbitRadius
|
||||
|
||||
// C. 闪烁计算 (呼吸感)
|
||||
star.twinklePhase += star.twinkleSpeed
|
||||
const twinkle = Math.sin(star.twinklePhase) * 0.2
|
||||
star.alpha = star.baseAlpha + twinkle
|
||||
|
||||
// 限制 alpha 范围
|
||||
if (star.alpha < 0) star.alpha = 0
|
||||
if (star.alpha > 1) star.alpha = 1
|
||||
|
||||
// 绘制
|
||||
ctx!.beginPath()
|
||||
ctx!.arc(star.x, star.y, star.radius, 0, Math.PI * 2)
|
||||
|
||||
// 只给大星星加一点辉光,增强设计感
|
||||
if (star.radius > 1.2) {
|
||||
ctx!.shadowBlur = 6
|
||||
ctx!.shadowColor = `rgba(255, 255, 255, ${star.alpha * 0.5})`
|
||||
if (star.fadingOut) {
|
||||
star.alpha -= star.speed
|
||||
if (star.alpha < 0) {
|
||||
star.alpha = 0
|
||||
star.fadingOut = false
|
||||
}
|
||||
} else {
|
||||
ctx!.shadowBlur = 0
|
||||
star.alpha += star.speed
|
||||
if (star.alpha > 1) {
|
||||
star.alpha = 1
|
||||
star.fadingOut = true
|
||||
}
|
||||
}
|
||||
|
||||
ctx!.fillStyle = `rgba(255, 255, 255, ${star.alpha})`
|
||||
ctx!.globalAlpha = star.alpha
|
||||
ctx!.beginPath()
|
||||
ctx!.arc(star.x, star.y, star.radius, 0, Math.PI * 2)
|
||||
ctx!.fill()
|
||||
})
|
||||
|
||||
ctx.shadowBlur = 0 // 重置辉光
|
||||
ctx.globalAlpha = 1
|
||||
|
||||
// 3. 绘制流星
|
||||
updateAndDrawMeteors()
|
||||
|
||||
// 4. 绘制波纹 (Shockwave)
|
||||
// 4. 绘制波纹 (Shockwave - 随机颜色)
|
||||
updateAndDrawWaves()
|
||||
|
||||
// 5. 绘制粒子 (Sparks)
|
||||
// 5. 绘制粒子 (Sparks - 物理效果)
|
||||
updateAndDrawParticles()
|
||||
|
||||
animationFrameId = requestAnimationFrame(render)
|
||||
@@ -182,7 +125,7 @@ const render = () => {
|
||||
// --- 辅助绘制函数 ---
|
||||
|
||||
const updateAndDrawMeteors = () => {
|
||||
if (Math.random() < 0.002) createMeteor() // 稍微降低自然流星频率,减少干扰
|
||||
if (Math.random() < config.meteorChance) createMeteor()
|
||||
|
||||
for (let i = meteors.length - 1; i >= 0; i--) {
|
||||
const m = meteors[i]
|
||||
@@ -191,9 +134,9 @@ const updateAndDrawMeteors = () => {
|
||||
const endX = m.x - m.length * Math.cos(m.angle)
|
||||
const endY = m.y - m.length * Math.sin(m.angle)
|
||||
|
||||
// 流星尾巴渐变
|
||||
const gradient = ctx.createLinearGradient(m.x, m.y, endX, endY)
|
||||
gradient.addColorStop(0, `rgba(255, 255, 255, ${m.alpha})`)
|
||||
gradient.addColorStop(0.2, `rgba(200, 230, 255, ${m.alpha * 0.8})`)
|
||||
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)')
|
||||
|
||||
ctx.beginPath()
|
||||
@@ -201,15 +144,13 @@ const updateAndDrawMeteors = () => {
|
||||
ctx.lineTo(endX, endY)
|
||||
ctx.lineWidth = m.width
|
||||
ctx.strokeStyle = gradient
|
||||
ctx.lineCap = 'round'
|
||||
ctx.stroke()
|
||||
|
||||
m.x += m.speed * Math.cos(m.angle)
|
||||
m.y += m.speed * Math.sin(m.angle)
|
||||
|
||||
if (m.x > width + 100 || m.x < -100 || m.y > height + 100 || m.y < -100) {
|
||||
m.alpha -= 0.02
|
||||
if (m.alpha <= 0) meteors.splice(i, 1)
|
||||
if (m.x > width + 100 || m.y > height + 100) {
|
||||
meteors.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,15 +162,16 @@ const updateAndDrawWaves = () => {
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(w.x, w.y, w.radius, 0, Math.PI * 2)
|
||||
ctx.strokeStyle = `hsla(${w.hue}, 80%, 70%, ${w.alpha})`
|
||||
// 使用 HSL 随机色
|
||||
ctx.strokeStyle = `hsla(${w.hue}, 70%, 60%, ${w.alpha})`
|
||||
ctx.lineWidth = w.lineWidth
|
||||
ctx.stroke()
|
||||
|
||||
w.radius += 2.5
|
||||
w.alpha *= 0.95
|
||||
w.radius += 2
|
||||
w.alpha -= 0.02
|
||||
w.lineWidth *= 0.95
|
||||
|
||||
if (w.alpha < 0.01) waves.splice(i, 1)
|
||||
if (w.alpha <= 0) waves.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,12 +187,12 @@ const updateAndDrawParticles = () => {
|
||||
|
||||
p.x += p.vx
|
||||
p.y += p.vy
|
||||
p.vy += 0.08
|
||||
p.vy += 0.05 // 重力
|
||||
p.vx *= 0.96
|
||||
p.vy *= 0.96
|
||||
|
||||
p.life -= 0.02
|
||||
p.size *= 0.95
|
||||
p.size *= 0.96
|
||||
|
||||
if (p.life <= 0) particles.splice(i, 1)
|
||||
}
|
||||
@@ -258,15 +200,15 @@ const updateAndDrawParticles = () => {
|
||||
|
||||
const createMeteor = (force = false) => {
|
||||
const startX = Math.random() * width
|
||||
const angle = Math.PI / 4 + (Math.random() - 0.5) * 0.3
|
||||
const angle = Math.PI / 4 // 45度
|
||||
meteors.push({
|
||||
x: startX,
|
||||
y: -50,
|
||||
length: Math.random() * 100 + 80,
|
||||
speed: Math.random() * 15 + 12,
|
||||
y: 0,
|
||||
length: Math.random() * 80 + 20,
|
||||
speed: Math.random() * 10 + 5,
|
||||
angle: angle,
|
||||
alpha: 1,
|
||||
width: Math.random() * 2 + 0.5
|
||||
width: Math.random() * 1.5 + 0.5
|
||||
})
|
||||
}
|
||||
|
||||
@@ -274,30 +216,33 @@ const createMeteor = (force = false) => {
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
const hue = Math.floor(Math.random() * 360)
|
||||
|
||||
// 1. 波纹
|
||||
waves.push({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
radius: 10,
|
||||
radius: 0,
|
||||
alpha: 1,
|
||||
lineWidth: 4,
|
||||
lineWidth: 3,
|
||||
hue: hue
|
||||
})
|
||||
|
||||
const particleCount = 16
|
||||
// 2. 粒子
|
||||
const particleCount = 10
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
const angle = (Math.PI * 2 / particleCount) * i + Math.random() * 0.5
|
||||
const speed = Math.random() * 5 + 2
|
||||
const angle = (Math.PI * 2 / particleCount) * i
|
||||
const speed = Math.random() * 3 + 1
|
||||
particles.push({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
vx: Math.cos(angle) * speed,
|
||||
vy: Math.sin(angle) * speed,
|
||||
life: 1.0,
|
||||
color: `hsla(${hue}, ${80 + Math.random() * 20}%, 70%, 1)`,
|
||||
size: Math.random() * 3 + 1
|
||||
color: `hsla(${hue}, 80%, 70%, 1)`, // 粒子跟随波纹颜色
|
||||
size: Math.random() * 2 + 1
|
||||
})
|
||||
}
|
||||
|
||||
// 3. 点击触发流星
|
||||
createMeteor(true)
|
||||
}
|
||||
|
||||
@@ -307,10 +252,6 @@ const handleResize = () => {
|
||||
height = window.innerHeight
|
||||
canvasRef.value.width = width
|
||||
canvasRef.value.height = height
|
||||
|
||||
centerX = width / 2
|
||||
centerY = height / 2
|
||||
|
||||
initStars()
|
||||
}
|
||||
}
|
||||
@@ -321,7 +262,6 @@ onMounted(() => {
|
||||
handleResize()
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
// Removed mousemove listener for pure atmospheric effect
|
||||
window.addEventListener('click', handleClick)
|
||||
|
||||
render()
|
||||
|
||||
Reference in New Issue
Block a user