|
| 1 | +/** |
| 2 | + * LeviathanShadow — 远洋巨影:对未知知识的敬畏 |
| 3 | + * 远景蝠鲼剪影,60~90 秒周期水平弧线缓慢滑过,深海雾中若隐若现 |
| 4 | + * 克制含蓄:透明度 0.1~0.15,单网格 + 平移,1 draw call |
| 5 | + * |
| 6 | + * @ai-context: 3D 场景对象:LeviathanShadow(DeepSeaWorld 增强层)。 |
| 7 | + * 宪法第四条:high 透明度 0.15 / medium 0.1 / low 关闭。 |
| 8 | + */ |
| 9 | +import { useMemo, useRef } from 'react'; |
| 10 | +import { useFrame } from '@react-three/fiber'; |
| 11 | +import * as THREE from 'three'; |
| 12 | +import { useEffectiveTier } from '@/lib/performance/usePerformanceMode'; |
| 13 | + |
| 14 | +/** 蝠鲼剪影轮廓(俯视 Shape):头部圆润、双翼展开、尾部细长 */ |
| 15 | +function createMantaShape(): THREE.Shape { |
| 16 | + const s = new THREE.Shape(); |
| 17 | + s.moveTo(0, 0.7); // 头部 |
| 18 | + s.quadraticCurveTo(2.4, 0.55, 3.2, 0); // 右翼展开 |
| 19 | + s.quadraticCurveTo(2.4, -0.55, 0.6, -0.3); // 右下回收 |
| 20 | + s.quadraticCurveTo(0.3, -0.75, 0, -0.85); // 尾基 |
| 21 | + s.quadraticCurveTo(-0.3, -0.75, -0.6, -0.3); // 左下 |
| 22 | + s.quadraticCurveTo(-2.4, -0.55, -3.2, 0); // 左翼展开 |
| 23 | + s.quadraticCurveTo(-2.4, 0.55, 0, 0.7); // 左上回收 |
| 24 | + return s; |
| 25 | +} |
| 26 | + |
| 27 | +/** 远洋巨影:远景剪影周期滑过 */ |
| 28 | +export function LeviathanShadow() { |
| 29 | + const tier = useEffectiveTier(); |
| 30 | + const meshRef = useRef<THREE.Mesh>(null); |
| 31 | + const timeRef = useRef(0); |
| 32 | + |
| 33 | + // 剪影几何:蝠鲼平面(单面,只从前方可见) |
| 34 | + const geometry = useMemo(() => new THREE.ShapeGeometry(createMantaShape(), 12), []); |
| 35 | + |
| 36 | + const opacity = tier === 'low' ? 0 : tier === 'medium' ? 0.1 : 0.15; |
| 37 | + // 周期:每只巨影一个 60~90 秒滑过周期 |
| 38 | + const cycle = useMemo(() => 60 + Math.random() * 30, []); |
| 39 | + // 滑过路径(弧线) |
| 40 | + const path = useMemo(() => ({ |
| 41 | + startX: -18 + Math.random() * 4, |
| 42 | + endX: 18 + Math.random() * 4, |
| 43 | + z: -20 - Math.random() * 10, |
| 44 | + y: 1 + Math.random() * 3, |
| 45 | + }), []); |
| 46 | + |
| 47 | + useFrame((_, delta) => { |
| 48 | + if (tier === 'low') return; |
| 49 | + if (!meshRef.current) return; |
| 50 | + const safeDelta = Math.min(delta, 0.1); |
| 51 | + timeRef.current += safeDelta; |
| 52 | + const u = (timeRef.current % cycle) / cycle; // 0→1 循环 |
| 53 | + |
| 54 | + // 水平弧线滑过 + 上下起伏 + 缓慢拍翼 |
| 55 | + meshRef.current.position.x = path.startX + (path.endX - path.startX) * u; |
| 56 | + meshRef.current.position.y = path.y + Math.sin(u * Math.PI * 2) * 0.8; |
| 57 | + meshRef.current.rotation.y = Math.sin(u * Math.PI) * 0.3; |
| 58 | + meshRef.current.rotation.z = Math.sin(u * Math.PI * 4) * 0.08; // 拍翼 |
| 59 | + }); |
| 60 | + |
| 61 | + if (tier === 'low') return null; |
| 62 | + |
| 63 | + return ( |
| 64 | + <mesh ref={meshRef} geometry={geometry} position={[path.startX, path.y, path.z]} scale={[8, 8, 8]}> |
| 65 | + <meshBasicMaterial |
| 66 | + color="#1E3A5F" transparent opacity={opacity} |
| 67 | + side={THREE.DoubleSide} depthWrite={false} |
| 68 | + /> |
| 69 | + </mesh> |
| 70 | + ); |
| 71 | +} |
0 commit comments