115 lines
3.8 KiB
HTML
115 lines
3.8 KiB
HTML
<style>
|
|
#canvas-stardust-container {
|
|
width: 100%; height: 100%; background: #020208; /* 非常暗的蓝黑色 */
|
|
overflow: hidden; position: relative;
|
|
}
|
|
#stardust-canvas { display: block; position: absolute; top: 0; left: 0; }
|
|
</style>
|
|
<div id="canvas-stardust-container">
|
|
<canvas id="stardust-canvas"></canvas>
|
|
</div>
|
|
<script>
|
|
(function() {
|
|
const canvas = document.getElementById('stardust-canvas');
|
|
const layerElement = canvas.closest('.terminal-custom-html-layer') || canvas.parentElement;
|
|
if (!canvas || !layerElement) return;
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
let width, height, particles = [];
|
|
const particleCount = 300; // 粒子数量,根据性能调整
|
|
const baseSpeed = 0.3;
|
|
const lifeSpan = 200; // 粒子生命周期(帧数)
|
|
const baseRadius = 0.5;
|
|
const radiusVariance = 1.5;
|
|
const colors = ["#FFFFFF", "#DDDDFF", "#BBBBFF", "#9999EE"]; // 星尘颜色
|
|
|
|
class Particle {
|
|
constructor() {
|
|
this.reset();
|
|
}
|
|
|
|
reset() {
|
|
this.x = Math.random() * width;
|
|
this.y = Math.random() * height; // 初始位置随机
|
|
// 或者让粒子从一个方向进入
|
|
// this.x = Math.random() * width;
|
|
// this.y = height + Math.random() * 100; // 从底部进入
|
|
|
|
|
|
this.vx = (Math.random() - 0.5) * baseSpeed * 0.5; // x方向轻微随机漂移
|
|
this.vy = -baseSpeed - Math.random() * baseSpeed; // 主要向上流动
|
|
|
|
this.life = Math.random() * lifeSpan * 0.5 + lifeSpan * 0.5;
|
|
this.initialLife = this.life;
|
|
this.radius = baseRadius + Math.random() * radiusVariance;
|
|
this.color = colors[Math.floor(Math.random() * colors.length)];
|
|
}
|
|
|
|
update() {
|
|
this.x += this.vx;
|
|
this.y += this.vy;
|
|
this.life--;
|
|
|
|
if (this.life <= 0 || this.y < -this.radius || this.x < -this.radius || this.x > width + this.radius) {
|
|
this.reset();
|
|
}
|
|
}
|
|
|
|
draw() {
|
|
ctx.beginPath();
|
|
// 透明度随生命周期变化,实现淡入淡出
|
|
const blinkFactor = Math.sin( (this.initialLife - this.life) * 0.1 + Math.random() * Math.PI ); // 模拟闪烁
|
|
const alpha = (this.life / this.initialLife) * 0.5 + 0.3 * blinkFactor;
|
|
|
|
ctx.globalAlpha = Math.max(0, Math.min(1, alpha));
|
|
ctx.fillStyle = this.color;
|
|
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
width = layerElement.offsetWidth;
|
|
height = layerElement.offsetHeight;
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
particles = [];
|
|
for (let i = 0; i < particleCount; i++) {
|
|
particles.push(new Particle());
|
|
}
|
|
}
|
|
|
|
let animationFrameId;
|
|
function animate() {
|
|
ctx.clearRect(0, 0, width, height);
|
|
ctx.globalAlpha = 1; // Reset globalAlpha for clearing
|
|
|
|
particles.forEach(p => {
|
|
p.update();
|
|
p.draw();
|
|
});
|
|
ctx.globalAlpha = 1; // Reset globalAlpha after drawing all particles
|
|
animationFrameId = requestAnimationFrame(animate);
|
|
}
|
|
|
|
let resizeTimeout;
|
|
function onResize() {
|
|
clearTimeout(resizeTimeout);
|
|
resizeTimeout = setTimeout(() => {
|
|
if (animationFrameId) cancelAnimationFrame(animationFrameId);
|
|
init();
|
|
animate();
|
|
}, 250);
|
|
}
|
|
|
|
init();
|
|
animate();
|
|
window.addEventListener('resize', onResize);
|
|
|
|
canvas.cleanup = function() {
|
|
if (animationFrameId) cancelAnimationFrame(animationFrameId);
|
|
window.removeEventListener('resize', onResize);
|
|
console.log("Stardust canvas cleaned up");
|
|
};
|
|
})();
|
|
</script> |