liuxing.html 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>唯美流星雨</title>
  7. <style>
  8. body, html {
  9. margin: 0;
  10. padding: 0;
  11. width: 100%;
  12. height: 100%;
  13. overflow: hidden;
  14. background-color: #020205; /* 备用背景色 */
  15. }
  16. /* 画布全屏 */
  17. canvas {
  18. display: block;
  19. }
  20. /* 一个简单的文字层,可选,增加氛围 */
  21. .overlay {
  22. position: absolute;
  23. bottom: 30px;
  24. width: 100%;
  25. text-align: center;
  26. color: rgba(255, 255, 255, 0.6);
  27. font-family: 'Segoe UI', sans-serif;
  28. font-size: 14px;
  29. pointer-events: none;
  30. letter-spacing: 2px;
  31. text-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
  32. }
  33. </style>
  34. </head>
  35. <body>
  36. <canvas id="sky"></canvas>
  37. <div class="overlay">静谧星空 · 流星划过</div>
  38. <script>
  39. const canvas = document.getElementById('sky');
  40. const ctx = canvas.getContext('2d');
  41. let width, height;
  42. let stars = [];
  43. let meteors = [];
  44. // 配置参数
  45. const config = {
  46. starCount: 200, // 背景星星数量
  47. meteorFrequency: 0.02, // 流星出现概率 (每帧)
  48. meteorSpeedMin: 2, // 流星最小速度 (设小一点实现缓慢效果)
  49. meteorSpeedMax: 4, // 流星最大速度
  50. meteorLength: 150, // 流星拖尾长度
  51. meteorAngle: 30 // 流星倾斜角度 (度)
  52. };
  53. // 初始化画布尺寸
  54. function resize() {
  55. width = window.innerWidth;
  56. height = window.innerHeight;
  57. canvas.width = width;
  58. canvas.height = height;
  59. createStars(); // 重置星星分布
  60. }
  61. window.addEventListener('resize', resize);
  62. // --- 星星类 (背景) ---
  63. class Star {
  64. constructor() {
  65. this.reset();
  66. }
  67. reset() {
  68. this.x = Math.random() * width;
  69. this.y = Math.random() * height;
  70. this.size = Math.random() * 1.5 + 0.5; // 大小
  71. this.blinkSpeed = Math.random() * 0.02 + 0.005; // 闪烁速度
  72. this.alpha = Math.random(); // 当前透明度
  73. this.blinkDir = Math.random() > 0.5 ? 1 : -1; // 变亮还是变暗
  74. }
  75. update() {
  76. // 简单的闪烁逻辑
  77. this.alpha += this.blinkSpeed * this.blinkDir;
  78. if (this.alpha >= 1) {
  79. this.alpha = 1;
  80. this.blinkDir = -1;
  81. } else if (this.alpha <= 0.2) {
  82. this.alpha = 0.2;
  83. this.blinkDir = 1;
  84. }
  85. }
  86. draw() {
  87. ctx.beginPath();
  88. ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
  89. ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
  90. ctx.fill();
  91. }
  92. }
  93. // --- 流星类 ---
  94. class Meteor {
  95. constructor() {
  96. this.reset();
  97. }
  98. reset() {
  99. // 随机生成位置:主要在右上方生成,向左下方划过
  100. // 为了保证流星能划过屏幕,起始 X 可以超过屏幕宽度,Y 可以是负数
  101. this.x = Math.random() * width + 200;
  102. this.y = Math.random() * height * 0.5 - 100;
  103. // 速度
  104. this.speed = Math.random() * (config.meteorSpeedMax - config.meteorSpeedMin) + config.meteorSpeedMin;
  105. // 长度也会稍微随机一点
  106. this.len = Math.random() * 50 + config.meteorLength;
  107. // 透明度
  108. this.alpha = 1;
  109. // 角度转弧度 (30度左右)
  110. this.angle = config.meteorAngle * (Math.PI / 180);
  111. // 计算移动增量
  112. this.dx = -Math.cos(this.angle) * this.speed;
  113. this.dy = Math.sin(this.angle) * this.speed;
  114. // 生命状态
  115. this.dead = false;
  116. }
  117. update() {
  118. this.x += this.dx;
  119. this.y += this.dy;
  120. // 如果完全移出屏幕左侧或底部,标记为死亡
  121. if (this.x < -this.len || this.y > height + this.len) {
  122. this.dead = true;
  123. }
  124. }
  125. draw() {
  126. // 计算尾巴坐标
  127. const tailX = this.x + Math.cos(this.angle) * this.len;
  128. const tailY = this.y - Math.sin(this.angle) * this.len;
  129. // 创建渐变:头部亮白,尾部透明
  130. const gradient = ctx.createLinearGradient(this.x, this.y, tailX, tailY);
  131. gradient.addColorStop(0, 'rgba(255, 255, 255, 1)'); // 头部
  132. gradient.addColorStop(0.1, 'rgba(255, 255, 255, 0.8)'); // 头部光晕
  133. gradient.addColorStop(0.4, 'rgba(173, 216, 230, 0.4)'); // 中段带点蓝
  134. gradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); // 尾部完全透明
  135. ctx.beginPath();
  136. ctx.moveTo(this.x, this.y);
  137. ctx.lineTo(tailX, tailY);
  138. ctx.lineWidth = 2;
  139. ctx.lineCap = 'round';
  140. ctx.strokeStyle = gradient;
  141. ctx.stroke();
  142. // 头部加个小光点,增加立体感
  143. ctx.beginPath();
  144. ctx.arc(this.x, this.y, 1.5, 0, Math.PI * 2);
  145. ctx.fillStyle = 'rgba(255, 255, 255, 1)';
  146. ctx.shadowBlur = 5;
  147. ctx.shadowColor = "white";
  148. ctx.fill();
  149. ctx.shadowBlur = 0; // 重置阴影,避免影响性能
  150. }
  151. }
  152. // --- 主逻辑 ---
  153. function createStars() {
  154. stars = [];
  155. for (let i = 0; i < config.starCount; i++) {
  156. stars.push(new Star());
  157. }
  158. }
  159. function drawBackground() {
  160. // 绘制深空渐变背景 (每一帧都重绘以覆盖上一帧)
  161. const grd = ctx.createLinearGradient(0, 0, 0, height);
  162. grd.addColorStop(0, "#050B14"); // 顶部:深邃黑蓝
  163. grd.addColorStop(0.5, "#0A1525"); // 中部:深蓝
  164. grd.addColorStop(1, "#122035"); // 底部:稍亮的蓝,模拟地平线光辉
  165. ctx.fillStyle = grd;
  166. ctx.fillRect(0, 0, width, height);
  167. }
  168. function animate() {
  169. drawBackground();
  170. // 绘制和更新星星
  171. stars.forEach(star => {
  172. star.update();
  173. star.draw();
  174. });
  175. // 随机生成新流星
  176. if (Math.random() < config.meteorFrequency) {
  177. meteors.push(new Meteor());
  178. }
  179. // 绘制和更新流星
  180. for (let i = meteors.length - 1; i >= 0; i--) {
  181. const m = meteors[i];
  182. m.update();
  183. m.draw();
  184. if (m.dead) {
  185. meteors.splice(i, 1);
  186. }
  187. }
  188. requestAnimationFrame(animate);
  189. }
  190. // 启动
  191. resize();
  192. animate();
  193. </script>
  194. </body>
  195. </html>