| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>唯美流星雨</title>
- <style>
- body, html {
- margin: 0;
- padding: 0;
- width: 100%;
- height: 100%;
- overflow: hidden;
- background-color: #020205; /* 备用背景色 */
- }
- /* 画布全屏 */
- canvas {
- display: block;
- }
- /* 一个简单的文字层,可选,增加氛围 */
- .overlay {
- position: absolute;
- bottom: 30px;
- width: 100%;
- text-align: center;
- color: rgba(255, 255, 255, 0.6);
- font-family: 'Segoe UI', sans-serif;
- font-size: 14px;
- pointer-events: none;
- letter-spacing: 2px;
- text-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
- }
- </style>
- </head>
- <body>
- <canvas id="sky"></canvas>
- <div class="overlay">静谧星空 · 流星划过</div>
- <script>
- const canvas = document.getElementById('sky');
- const ctx = canvas.getContext('2d');
- let width, height;
- let stars = [];
- let meteors = [];
- // 配置参数
- const config = {
- starCount: 200, // 背景星星数量
- meteorFrequency: 0.02, // 流星出现概率 (每帧)
- meteorSpeedMin: 2, // 流星最小速度 (设小一点实现缓慢效果)
- meteorSpeedMax: 4, // 流星最大速度
- meteorLength: 150, // 流星拖尾长度
- meteorAngle: 30 // 流星倾斜角度 (度)
- };
- // 初始化画布尺寸
- function resize() {
- width = window.innerWidth;
- height = window.innerHeight;
- canvas.width = width;
- canvas.height = height;
- createStars(); // 重置星星分布
- }
- window.addEventListener('resize', resize);
- // --- 星星类 (背景) ---
- class Star {
- constructor() {
- this.reset();
- }
- reset() {
- this.x = Math.random() * width;
- this.y = Math.random() * height;
- this.size = Math.random() * 1.5 + 0.5; // 大小
- this.blinkSpeed = Math.random() * 0.02 + 0.005; // 闪烁速度
- this.alpha = Math.random(); // 当前透明度
- this.blinkDir = Math.random() > 0.5 ? 1 : -1; // 变亮还是变暗
- }
- update() {
- // 简单的闪烁逻辑
- this.alpha += this.blinkSpeed * this.blinkDir;
- if (this.alpha >= 1) {
- this.alpha = 1;
- this.blinkDir = -1;
- } else if (this.alpha <= 0.2) {
- this.alpha = 0.2;
- this.blinkDir = 1;
- }
- }
- draw() {
- ctx.beginPath();
- ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
- ctx.fill();
- }
- }
- // --- 流星类 ---
- class Meteor {
- constructor() {
- this.reset();
- }
- reset() {
- // 随机生成位置:主要在右上方生成,向左下方划过
- // 为了保证流星能划过屏幕,起始 X 可以超过屏幕宽度,Y 可以是负数
- this.x = Math.random() * width + 200;
- this.y = Math.random() * height * 0.5 - 100;
-
- // 速度
- this.speed = Math.random() * (config.meteorSpeedMax - config.meteorSpeedMin) + config.meteorSpeedMin;
-
- // 长度也会稍微随机一点
- this.len = Math.random() * 50 + config.meteorLength;
-
- // 透明度
- this.alpha = 1;
-
- // 角度转弧度 (30度左右)
- this.angle = config.meteorAngle * (Math.PI / 180);
-
- // 计算移动增量
- this.dx = -Math.cos(this.angle) * this.speed;
- this.dy = Math.sin(this.angle) * this.speed;
- // 生命状态
- this.dead = false;
- }
- update() {
- this.x += this.dx;
- this.y += this.dy;
- // 如果完全移出屏幕左侧或底部,标记为死亡
- if (this.x < -this.len || this.y > height + this.len) {
- this.dead = true;
- }
- }
- draw() {
- // 计算尾巴坐标
- const tailX = this.x + Math.cos(this.angle) * this.len;
- const tailY = this.y - Math.sin(this.angle) * this.len;
- // 创建渐变:头部亮白,尾部透明
- const gradient = ctx.createLinearGradient(this.x, this.y, tailX, tailY);
- gradient.addColorStop(0, 'rgba(255, 255, 255, 1)'); // 头部
- gradient.addColorStop(0.1, 'rgba(255, 255, 255, 0.8)'); // 头部光晕
- gradient.addColorStop(0.4, 'rgba(173, 216, 230, 0.4)'); // 中段带点蓝
- gradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); // 尾部完全透明
- ctx.beginPath();
- ctx.moveTo(this.x, this.y);
- ctx.lineTo(tailX, tailY);
- ctx.lineWidth = 2;
- ctx.lineCap = 'round';
- ctx.strokeStyle = gradient;
- ctx.stroke();
- // 头部加个小光点,增加立体感
- ctx.beginPath();
- ctx.arc(this.x, this.y, 1.5, 0, Math.PI * 2);
- ctx.fillStyle = 'rgba(255, 255, 255, 1)';
- ctx.shadowBlur = 5;
- ctx.shadowColor = "white";
- ctx.fill();
- ctx.shadowBlur = 0; // 重置阴影,避免影响性能
- }
- }
- // --- 主逻辑 ---
- function createStars() {
- stars = [];
- for (let i = 0; i < config.starCount; i++) {
- stars.push(new Star());
- }
- }
- function drawBackground() {
- // 绘制深空渐变背景 (每一帧都重绘以覆盖上一帧)
- const grd = ctx.createLinearGradient(0, 0, 0, height);
- grd.addColorStop(0, "#050B14"); // 顶部:深邃黑蓝
- grd.addColorStop(0.5, "#0A1525"); // 中部:深蓝
- grd.addColorStop(1, "#122035"); // 底部:稍亮的蓝,模拟地平线光辉
- ctx.fillStyle = grd;
- ctx.fillRect(0, 0, width, height);
- }
- function animate() {
- drawBackground();
- // 绘制和更新星星
- stars.forEach(star => {
- star.update();
- star.draw();
- });
- // 随机生成新流星
- if (Math.random() < config.meteorFrequency) {
- meteors.push(new Meteor());
- }
- // 绘制和更新流星
- for (let i = meteors.length - 1; i >= 0; i--) {
- const m = meteors[i];
- m.update();
- m.draw();
- if (m.dead) {
- meteors.splice(i, 1);
- }
- }
- requestAnimationFrame(animate);
- }
- // 启动
- resize();
- animate();
- </script>
- </body>
- </html>
|