- 主页第4行特色动画(版本控制)及 01/02/03 动画迭代 - 演示区对话 mock 全部换为真实对话转换:日常使用(汽车对比新/整理周会速记)、 StarRaid(弹幕调研/像素飞机设计)、开发实战(猫咪咖啡店多智能体) - 子智能体 activity、消息截图媒体、文件卡片资源(docx/pdf/html)随站点发布 - sw.js: file/content 二进制 MIME、/api/conversations/media 路由、 模型列表扩为 K3/V4-Flash/MiniMax-M3/GLM-5.2、CACHE v5 - content/ 同步 8-31 沙箱重构文档更新
289 lines
9.2 KiB
JavaScript
289 lines
9.2 KiB
JavaScript
/* 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'));
|
||
}
|
||
|
||
/* ═══════════ 8) 修改记录动画(功能 03 行) ═══════════ */
|
||
|
||
if (window.AnimRecords && document.getElementById('animRecords')) {
|
||
AnimRecords.init(document.getElementById('animRecords'));
|
||
}
|
||
|
||
/* ═══════════ 9) 版本控制动画(功能 04 行) ═══════════ */
|
||
|
||
if (window.AnimVersion && document.getElementById('animVersion')) {
|
||
AnimVersion.init(document.getElementById('animVersion'));
|
||
}
|
||
})();
|