/* Astrion 官网主页交互 * 1) 多层视差星空(无连线、无光晕:实心星点 + 闪烁 + 极慢漂移 + 鼠标/滚动视差 + 偶发流星) * 2) 顶栏滚动态 * 3) FAQ 手风琴平滑展开(details + 高度动画) * 4) 演示区 iframe 等比缩放(内部固定 1400×860 逻辑尺寸) * 5) 挂载品牌眼睛(眨眼 + 鼠标追踪) */ (function () { 'use strict'; var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; /* ═══════════ 1) 星空 ═══════════ */ var canvas = document.getElementById('starfield'); var ctx = canvas.getContext('2d'); var W = 0; var H = 0; var DPR = Math.min(window.devicePixelRatio || 1, 2); // 层:远 → 近。count 按屏幕面积缩放,drift 为每秒下落 px,parallax 为鼠标/滚动视差系数 var LAYERS = [ { density: 1 / 5200, sizeMin: 0.4, sizeMax: 0.9, drift: 1.2, parallax: 6, twinkle: 0.35, alpha: 0.55 }, { density: 1 / 9800, sizeMin: 0.7, sizeMax: 1.3, drift: 2.6, parallax: 14, twinkle: 0.55, alpha: 0.75 }, { density: 1 / 21000, sizeMin: 1.1, sizeMax: 1.9, drift: 4.5, parallax: 26, twinkle: 0.8, alpha: 1.0 } ]; var stars = []; var meteors = []; var nextMeteorAt = 0; function resize() { W = window.innerWidth; H = window.innerHeight; canvas.width = Math.round(W * DPR); canvas.height = Math.round(H * DPR); canvas.style.width = W + 'px'; canvas.style.height = H + 'px'; ctx.setTransform(DPR, 0, 0, DPR, 0, 0); seed(); if (reduceMotion) draw(0, 0, 0); } function seed() { stars = []; var area = W * H; for (var li = 0; li < LAYERS.length; li++) { var L = LAYERS[li]; var n = Math.max(12, Math.round(area * L.density)); for (var i = 0; i < n; i++) { stars.push({ layer: li, x: Math.random() * W, y: Math.random() * H, r: L.sizeMin + Math.random() * (L.sizeMax - L.sizeMin), // 闪烁相位与速度(部分星几乎不闪,画面才稳) phase: Math.random() * Math.PI * 2, speed: 0.25 + Math.random() * 0.9, amp: 0.25 + Math.random() * 0.75 }); } } } // 鼠标视差目标值 var mouseTx = 0; var mouseTy = 0; var mouseX = 0; var mouseY = 0; window.addEventListener('mousemove', function (e) { mouseTx = e.clientX / W - 0.5; mouseTy = e.clientY / H - 0.5; }, { passive: true }); function scheduleMeteor(now) { // 22~48 秒一颗,保持稀有 nextMeteorAt = now + 22000 + Math.random() * 26000; } function spawnMeteor() { var fromLeft = Math.random() < 0.5; var startX = fromLeft ? -40 : W * (0.35 + Math.random() * 0.65); var startY = Math.random() * H * 0.4; var angle = fromLeft ? 0.32 + Math.random() * 0.2 : Math.PI - (0.32 + Math.random() * 0.2); var speed = 380 + Math.random() * 240; meteors.push({ x: startX, y: startY, vx: Math.cos(angle) * speed, vy: Math.abs(Math.sin(angle)) * speed, life: 0, ttl: 0.7 + Math.random() * 0.35 }); } var lastTs = 0; function tick(ts) { if (document.hidden) { lastTs = ts; requestAnimationFrame(tick); return; } var dt = Math.min((ts - lastTs) / 1000 || 0, 0.05); lastTs = ts; var now = ts; // 鼠标视差缓动 mouseX += (mouseTx - mouseX) * 0.04; mouseY += (mouseTy - mouseY) * 0.04; if (now >= nextMeteorAt) { spawnMeteor(); scheduleMeteor(now); } draw(dt, ts, window.scrollY || 0); // 流星生命推进 for (var i = meteors.length - 1; i >= 0; i--) { var m = meteors[i]; m.life += dt; m.x += m.vx * dt; m.y += m.vy * dt; if (m.life > m.ttl || m.y > H + 60) meteors.splice(i, 1); } requestAnimationFrame(tick); } function draw(dt, ts, scrollY) { ctx.clearRect(0, 0, W, H); var t = ts / 1000; for (var i = 0; i < stars.length; i++) { var s = stars[i]; var L = LAYERS[s.layer]; if (dt) { s.y += L.drift * dt; if (s.y > H + 4) { s.y = -4; s.x = Math.random() * W; } } // 视差:滚动让星野整体上移(近处更明显),鼠标让层间错动 var px = s.x + mouseX * L.parallax * 2; var py = s.y - (scrollY * L.parallax * 0.012) + mouseY * L.parallax; // 纵向包裹,保证滚动后星点仍铺满 var wrappedY = ((py % (H + 8)) + (H + 8)) % (H + 8) - 4; var tw = 1 - L.twinkle * 0.5 + L.twinkle * 0.5 * Math.sin(t * s.speed + s.phase) * s.amp; var a = L.alpha * Math.max(0.15, Math.min(1, tw)); ctx.globalAlpha = a; ctx.fillStyle = '#dfe6f5'; ctx.fillRect(px, wrappedY, s.r, s.r); } // 流星:短细线段,头部略亮(线性渐变线条,不是光晕) for (var j = 0; j < meteors.length; j++) { var m = meteors[j]; var fade = 1 - m.life / m.ttl; var tailX = m.x - m.vx * 0.14; var tailY = m.y - m.vy * 0.14; var grad = ctx.createLinearGradient(tailX, tailY, m.x, m.y); grad.addColorStop(0, 'rgba(223,230,245,0)'); grad.addColorStop(1, 'rgba(223,230,245,' + (0.6 * fade).toFixed(3) + ')'); ctx.globalAlpha = 1; ctx.strokeStyle = grad; ctx.lineWidth = 1.1; ctx.beginPath(); ctx.moveTo(tailX, tailY); ctx.lineTo(m.x, m.y); ctx.stroke(); } ctx.globalAlpha = 1; } resize(); window.addEventListener('resize', resize, { passive: true }); if (reduceMotion) { draw(0, 0, 0); // 静态星图 } else { scheduleMeteor(performance.now() + 6000); // 首颗稍提前,证明存在 requestAnimationFrame(tick); // 滚动时重绘由 rAF 持续驱动,无需 scroll 监听 } /* ═══════════ 2) 顶栏滚动态 ═══════════ */ var topbar = document.getElementById('topbar'); function onScroll() { topbar.classList.toggle('scrolled', (window.scrollY || 0) > 24); } window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); /* ═══════════ 3) FAQ 平滑手风琴 ═══════════ */ var faqItems = document.querySelectorAll('.faq-item'); faqItems.forEach(function (item) { var summary = item.querySelector('.faq-q'); var panel = item.querySelector('.faq-a'); summary.addEventListener('click', function (e) { e.preventDefault(); if (reduceMotion) { item.open = !item.open; return; } if (item.open) { // 收起:先固定高度再过渡到 0 var h0 = panel.offsetHeight; panel.style.height = h0 + 'px'; requestAnimationFrame(function () { panel.style.transition = 'height 0.22s ease'; panel.style.height = '0px'; }); panel.addEventListener('transitionend', function done() { panel.removeEventListener('transitionend', done); panel.style.cssText = ''; item.open = false; }); } else { item.open = true; var h1 = panel.offsetHeight; panel.style.height = '0px'; requestAnimationFrame(function () { panel.style.transition = 'height 0.24s ease'; panel.style.height = h1 + 'px'; }); panel.addEventListener('transitionend', function done() { panel.removeEventListener('transitionend', done); panel.style.cssText = ''; }); } }); }); /* ═══════════ 4) 演示区等比缩放 ═══════════ */ var frame = document.getElementById('demoFrame'); var iframe = document.getElementById('demoIframe'); var DEMO_W = 1400; var DEMO_H = 860; function fitDemo() { var available = frame.clientWidth; var scale = Math.min(1, available / DEMO_W); iframe.style.transform = 'scale(' + scale + ')'; frame.style.height = Math.round(DEMO_H * scale) + 'px'; } if ('ResizeObserver' in window) { new ResizeObserver(fitDemo).observe(frame); } window.addEventListener('resize', fitDemo, { passive: true }); fitDemo(); /* ═══════════ 5) 品牌眼睛 ═══════════ */ if (window.AstrionAvatar) { AstrionAvatar.mount(document.getElementById('heroMascot'), { mode: 'idle', blink: true, track: true }); } /* ═══════════ 6) 运行模式动画(功能 01 行) ═══════════ */ if (window.AnimModes && document.getElementById('animModes')) { AnimModes.init(document.getElementById('animModes')); } /* ═══════════ 7) 沙箱与网络授权动画(功能 02 行) ═══════════ */ if (window.AnimSandbox && document.getElementById('animSandbox')) { AnimSandbox.init(document.getElementById('animSandbox')); } })();