Bladeren bron

ai liuxing

Carl 1 dag geleden
bovenliggende
commit
5a0de215e3
2 gewijzigde bestanden met toevoegingen van 569 en 0 verwijderingen
  1. 231 0
      liuxing.html
  2. 338 0
      src/components/AiorzWebARLanding.tsx

+ 231 - 0
liuxing.html

@@ -0,0 +1,231 @@
+<!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>

+ 338 - 0
src/components/AiorzWebARLanding.tsx

@@ -80,6 +80,20 @@ const AiorzWebARLanding = () => {
 
   const shapesRef = useRef<Record<string, Float32Array>>({});
 
+  // Meteor System Refs
+  const meteorsRef = useRef<{
+    meteors: Array<{
+      head: any; // 头部点
+      tail: any; // 尾巴线条
+      headPos: { x: number; y: number; z: number };
+      velocity: { x: number; y: number; z: number };
+      tailPoints: Array<{ x: number; y: number; z: number }>;
+      startDelay: number; // 延迟启动
+    }>;
+    count: number;
+    frameCount?: number; // 帧计数器
+  } | null>(null);
+
   // Configuration
   const CONFIG = {
     particleCount: 16000,
@@ -154,8 +168,11 @@ const AiorzWebARLanding = () => {
     renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
     rendererRef.current = renderer;
 
+    console.log('[ThreeJS] Initialized, creating particles and meteors');
     createParticles(THREE);
     initShapes(THREE);
+    createMeteors(THREE);
+    console.log('[ThreeJS] All systems initialized');
 
     window.addEventListener('resize', () => {
       camera.aspect = window.innerWidth / window.innerHeight;
@@ -227,6 +244,194 @@ const AiorzWebARLanding = () => {
     particlesDataRef.current = { positions, velocities, colors, targetPositions, targetColors };
   };
 
+  // ==================== 2.5. Meteor System ====================
+  const createMeteors = (THREE: any) => {
+    console.log('[Meteor] createMeteors called');
+    const meteorCount = 250; // 大量流星数量
+    
+    const camera = cameraRef.current;
+    if (!camera) {
+      console.error('[Meteor] Camera not initialized!');
+      return;
+    }
+    
+    const distance = 15;
+    const fov = 75;
+    const aspect = window.innerWidth / window.innerHeight;
+    const vFov = (fov * Math.PI) / 180;
+    const heightAtDistance = 2 * Math.tan(vFov / 2) * distance;
+    const widthAtDistance = heightAtDistance * aspect;
+    
+    const baseAngle = Math.PI / 4; // 45度角
+    const baseSpeed = 0.01; // 缓慢的星际速度(减半)
+    const tailLength = 20; // 尾巴长度(点数)
+    
+    const meteors: Array<{
+      head: any;
+      tail: any;
+      headPos: { x: number; y: number; z: number };
+      velocity: { x: number; y: number; z: number };
+      tailPoints: Array<{ x: number; y: number; z: number }>;
+      startDelay: number; // 延迟启动,让流星错开出现
+    }> = [];
+    
+    // 创建头部发光纹理(参考 liuxing.html:纯白光点带阴影效果)
+    const headCanvas = document.createElement('canvas');
+    headCanvas.width = 32;
+    headCanvas.height = 32;
+    const headCtx = headCanvas.getContext('2d');
+    if (headCtx) {
+      // 参考 liuxing.html:头部是纯白光点
+      const grad = headCtx.createRadialGradient(16, 16, 0, 16, 16, 16);
+      grad.addColorStop(0, 'rgba(255, 255, 255, 1)'); // 纯白核心(最亮)
+      grad.addColorStop(0.3, 'rgba(255, 255, 255, 0.8)'); // 白色光晕
+      grad.addColorStop(0.6, 'rgba(173, 216, 230, 0.4)'); // 中段带点蓝(参考 liuxing.html)
+      grad.addColorStop(1, 'rgba(255, 255, 255, 0)'); // 透明边缘
+      headCtx.fillStyle = grad;
+      headCtx.fillRect(0, 0, 32, 32);
+    }
+    const headTexture = new THREE.Texture(headCanvas);
+    headTexture.needsUpdate = true;
+    
+    // 为每个流星创建头部和尾巴
+    for (let m = 0; m < meteorCount; m++) {
+      // 起始位置:从屏幕右上角外开始,分散在不同位置
+      // 让流星从屏幕外开始,这样它们会陆续进入视野
+      // 调整位置范围,确保流星在可见区域内或附近
+      const startX = widthAtDistance * (0.3 + Math.random() * 1.2); // 从屏幕内到屏幕外
+      const startY = heightAtDistance * (-0.2 + Math.random() * 0.8); // 从屏幕上方外到屏幕内
+      const startZ = distance - 2 - Math.random() * 6; // 更靠近相机
+      
+      // 速度(略有变化)
+      const speedVariation = 0.8 + Math.random() * 0.4;
+      const velX = -Math.cos(baseAngle) * baseSpeed * speedVariation;
+      const velY = -Math.sin(baseAngle) * baseSpeed * speedVariation;
+      const velZ = 0;
+      
+      // 延迟启动,让流星错开出现(大部分立即显示,少数延迟)
+      const startDelay = m < 50 ? 0 : Math.random() * 10; // 前50个立即显示,其他的短延迟
+      
+      // 创建头部点
+      const headGeometry = new THREE.BufferGeometry();
+      const headPositions = new Float32Array([startX, startY, startZ]);
+      headGeometry.setAttribute('position', new THREE.BufferAttribute(headPositions, 3));
+      
+      const headMaterial = new THREE.PointsMaterial({
+        size: 3.0, // 参考 liuxing.html:头部光点较小(1.5像素半径,这里用3.0)
+        map: headTexture,
+        transparent: true,
+        opacity: 1.0,
+        blending: THREE.AdditiveBlending,
+        depthWrite: false,
+        color: new THREE.Color(1, 1, 1) // 纯白色(参考 liuxing.html)
+      });
+      
+      const head = new THREE.Points(headGeometry, headMaterial);
+      
+      // 创建尾巴线条(使用渐变颜色)
+      // 倒着飞:尾巴在前面(运动方向的前方),头部在后面
+      const tailPoints: Array<{ x: number; y: number; z: number }> = [];
+      for (let t = 0; t < tailLength; t++) {
+        const tailOffset = t * 0.5; // 增加尾巴点间距,让尾巴更长
+        // 尾巴在运动方向的前方(向左下方向)
+        tailPoints.push({
+          x: startX - Math.cos(baseAngle) * tailOffset, // 向左下方向延伸
+          y: startY - Math.sin(baseAngle) * tailOffset,
+          z: startZ
+        });
+      }
+      
+      // 创建尾巴线条(使用渐变颜色)
+      const tailGeometry = new THREE.BufferGeometry();
+      const tailPositions = new Float32Array(tailLength * 3);
+      const tailColors = new Float32Array(tailLength * 3);
+      
+      for (let t = 0; t < tailLength; t++) {
+        const idx = t * 3;
+        tailPositions[idx] = tailPoints[t].x;
+        tailPositions[idx + 1] = tailPoints[t].y;
+        tailPositions[idx + 2] = tailPoints[t].z;
+        
+        // 颜色渐变:倒着飞,尾部在前面(最亮),头部在后面(较暗)
+        // 反转渐变:从尾部(t=0,最亮)到头部(t=tailLength-1,较暗)
+        const progress = t / (tailLength - 1);
+        const reverseProgress = 1.0 - progress; // 反转进度:尾部是0,头部是1
+        let r, g, b, alpha;
+        
+        if (reverseProgress <= 0.1) {
+          // 尾部区域(倒着飞时在前面):纯白,高不透明度
+          const localProgress = reverseProgress / 0.1;
+          alpha = 1.0 - localProgress * 0.2; // 1.0 到 0.8
+          r = 1.0;
+          g = 1.0;
+          b = 1.0;
+        } else if (reverseProgress <= 0.4) {
+          // 中段:从白色过渡到淡蓝色
+          const localProgress = (reverseProgress - 0.1) / 0.3;
+          alpha = 0.8 - localProgress * 0.4; // 0.8 到 0.4
+          r = 1.0 - localProgress * 0.25; // 1.0 到 0.75 (173/255)
+          g = 1.0 - localProgress * 0.15; // 1.0 到 0.85 (216/255)
+          b = 1.0 - localProgress * 0.1;  // 1.0 到 0.9 (230/255)
+        } else {
+          // 头部(倒着飞时在后面):快速变透明
+          const localProgress = (reverseProgress - 0.4) / 0.6;
+          alpha = 0.4 * (1.0 - localProgress); // 0.4 到 0
+          r = 0.75 + localProgress * 0.25; // 保持淡蓝白色
+          g = 0.85 + localProgress * 0.15;
+          b = 0.9 + localProgress * 0.1;
+        }
+        
+        // 将 alpha 应用到 RGB(因为 vertexColors 不支持单独的 alpha)
+        r = r * alpha;
+        g = g * alpha;
+        b = b * alpha;
+        
+        tailColors[idx] = r;
+        tailColors[idx + 1] = g;
+        tailColors[idx + 2] = b;
+      }
+      
+      tailGeometry.setAttribute('position', new THREE.BufferAttribute(tailPositions, 3));
+      tailGeometry.setAttribute('color', new THREE.BufferAttribute(tailColors, 3));
+      
+      const tailMaterial = new THREE.LineBasicMaterial({
+        vertexColors: true,
+        transparent: true,
+        opacity: 1.0, // 参考 liuxing.html:使用 vertexColors 控制透明度
+        blending: THREE.AdditiveBlending,
+        linewidth: 2, // 参考 liuxing.html:lineWidth = 2
+        depthWrite: false
+      });
+      
+      const tail = new THREE.Line(tailGeometry, tailMaterial);
+      
+      // 根据延迟决定是否立即显示
+      head.visible = startDelay === 0;
+      tail.visible = startDelay === 0;
+      
+      sceneRef.current.add(head);
+      sceneRef.current.add(tail);
+      
+      meteors.push({
+        head,
+        tail,
+        headPos: { x: startX, y: startY, z: startZ },
+        velocity: { x: velX, y: velY, z: velZ },
+        tailPoints,
+        startDelay
+      });
+    }
+    
+    console.log('[Meteor] Created', meteorCount, 'meteors with heads and tails');
+    
+    meteorsRef.current = {
+      meteors,
+      count: meteorCount
+    };
+    
+    console.log('[Meteor] Meteor system initialized successfully');
+  };
+
   // ==================== 3. Shape Generation ====================
   const initShapes = (THREE: any) => {
     const count = CONFIG.particleCount;
@@ -554,6 +759,139 @@ const AiorzWebARLanding = () => {
     particles.geometry.attributes.color.needsUpdate = true;
     particles.rotation.y += 0.001;
 
+    // Update meteors - 恒定流星动画(带尾巴)
+    if (meteorsRef.current) {
+      const { meteors, count } = meteorsRef.current;
+      
+      // 计算可见区域(3D 空间)
+      const distance = 15;
+      const fov = 75;
+      const aspect = window.innerWidth / window.innerHeight;
+      const vFov = (fov * Math.PI) / 180;
+      const heightAtDistance = 2 * Math.tan(vFov / 2) * distance;
+      const widthAtDistance = heightAtDistance * aspect;
+      
+      const baseAngle = Math.PI / 4; // 45度角
+      const tailLength = 20;
+      
+      // 使用帧计数器来跟踪延迟
+      if (!meteorsRef.current.frameCount) {
+        meteorsRef.current.frameCount = 0;
+      }
+      meteorsRef.current.frameCount++;
+      
+      for (let m = 0; m < count; m++) {
+        const meteor = meteors[m];
+        
+        // 检查是否到了启动时间
+        if (meteorsRef.current.frameCount < meteor.startDelay) {
+          continue; // 还没到启动时间,跳过
+        }
+        
+        // 启动流星(显示)
+        if (!meteor.head.visible) {
+          meteor.head.visible = true;
+          meteor.tail.visible = true;
+        }
+        
+        const vel = meteor.velocity;
+        
+        // 更新头部位置
+        meteor.headPos.x += vel.x;
+        meteor.headPos.y += vel.y;
+        meteor.headPos.z += vel.z;
+        
+        // 更新头部点位置(倒着飞:头部在后面)
+        const headPositions = meteor.head.geometry.attributes.position;
+        // 头部在运动方向的后方
+        headPositions.array[0] = meteor.headPos.x + Math.cos(baseAngle + Math.PI) * 2.0; // 头部在后面
+        headPositions.array[1] = meteor.headPos.y + Math.sin(baseAngle + Math.PI) * 2.0;
+        headPositions.array[2] = meteor.headPos.z;
+        headPositions.needsUpdate = true;
+        
+        // 更新尾巴:倒着飞,尾巴在前面(运动方向的前方)
+        const tailPositions = meteor.tail.geometry.attributes.position;
+        const tailColors = meteor.tail.geometry.attributes.color;
+        
+        for (let t = 0; t < tailLength; t++) {
+          const idx = t * 3;
+          const tailOffset = t * 0.5; // 增加尾巴间距
+          
+          // 尾巴点位置:在运动方向的前方(向左下方向延伸)
+          tailPositions.array[idx] = meteor.headPos.x - Math.cos(baseAngle) * tailOffset;
+          tailPositions.array[idx + 1] = meteor.headPos.y - Math.sin(baseAngle) * tailOffset;
+          tailPositions.array[idx + 2] = meteor.headPos.z;
+          
+          // 更新尾巴颜色渐变(倒着飞:尾部在前面最亮,头部在后面较暗)
+          const progress = t / (tailLength - 1);
+          const reverseProgress = 1.0 - progress; // 反转进度
+          let r, g, b, alpha;
+          
+          if (reverseProgress <= 0.1) {
+            // 尾部区域(倒着飞时在前面):纯白,高不透明度
+            const localProgress = reverseProgress / 0.1;
+            alpha = 1.0 - localProgress * 0.2; // 1.0 到 0.8
+            r = 1.0;
+            g = 1.0;
+            b = 1.0;
+          } else if (reverseProgress <= 0.4) {
+            // 中段:从白色过渡到淡蓝色
+            const localProgress = (reverseProgress - 0.1) / 0.3;
+            alpha = 0.8 - localProgress * 0.4; // 0.8 到 0.4
+            r = 1.0 - localProgress * 0.25; // 1.0 到 0.75 (173/255)
+            g = 1.0 - localProgress * 0.15; // 1.0 到 0.85 (216/255)
+            b = 1.0 - localProgress * 0.1;  // 1.0 到 0.9 (230/255)
+          } else {
+            // 头部(倒着飞时在后面):快速变透明
+            const localProgress = (reverseProgress - 0.4) / 0.6;
+            alpha = 0.4 * (1.0 - localProgress); // 0.4 到 0
+            r = 0.75 + localProgress * 0.25; // 保持淡蓝白色
+            g = 0.85 + localProgress * 0.15;
+            b = 0.9 + localProgress * 0.1;
+          }
+          
+          // 将 alpha 应用到 RGB
+          r = r * alpha;
+          g = g * alpha;
+          b = b * alpha;
+          
+          tailColors.array[idx] = r;
+          tailColors.array[idx + 1] = g;
+          tailColors.array[idx + 2] = b;
+        }
+        
+        tailPositions.needsUpdate = true;
+        tailColors.needsUpdate = true;
+        
+        // 如果流星飞出屏幕,重置到右上角外
+        const leftBound = -widthAtDistance * 0.8;
+        const bottomBound = -heightAtDistance * 0.8;
+        
+        if (meteor.headPos.x < leftBound || meteor.headPos.y < bottomBound) {
+          // 从右上角外重新开始(确保在可见区域)
+          meteor.headPos.x = widthAtDistance * (0.3 + Math.random() * 1.2);
+          meteor.headPos.y = heightAtDistance * (-0.2 + Math.random() * 0.8);
+          meteor.headPos.z = distance - 2 - Math.random() * 6;
+          
+          // 重置延迟,让流星错开出现(减少延迟,增加频率)
+          meteor.startDelay = meteorsRef.current.frameCount + Math.random() * 5; // 非常短的延迟
+          
+          // 立即显示(因为延迟很短)
+          meteor.head.visible = true;
+          meteor.tail.visible = true;
+          
+          // 重置尾巴点(倒着飞:尾巴在前面)
+          for (let t = 0; t < tailLength; t++) {
+            meteor.tailPoints[t] = {
+              x: meteor.headPos.x - Math.cos(baseAngle) * t * 0.5, // 尾巴在运动方向前方
+              y: meteor.headPos.y - Math.sin(baseAngle) * t * 0.5,
+              z: meteor.headPos.z
+            };
+          }
+        }
+      }
+    }
+
     rendererRef.current.render(sceneRef.current, cameraRef.current);
   };