- 首页: Three.js破碎月亮太空之旅(warp入场/碎片悬浮/代码泄漏/腐化标题) - 关于: 地面控制中心 - 产品: 碎片修复舱(加密悬念) - 联系: 通讯链路 - 纯静态: HTML+CSS+原生JS+Three.js本地化
488 lines
18 KiB
JavaScript
488 lines
18 KiB
JavaScript
/* ============================================================
|
|
晟算科技 · 太空场景引擎
|
|
破碎的月亮 = 失控的软件世界 | 修复进度 85% 停住
|
|
模式: journey(首页滚动驱动) / orbit(子页面轨道展示)
|
|
============================================================ */
|
|
|
|
window.SPACE_MODE = document.body.getAttribute("data-space") || "journey";
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
var isMobile = window.innerWidth < 768 || "ontouchstart" in window;
|
|
var reducedMotion = window.matchMedia("(prefers-reduced-motion: reduced)").matches;
|
|
var bootEl = document.getElementById("spaceBoot");
|
|
var canvasHost = document.getElementById("spaceCanvas");
|
|
|
|
/* ---------- WebGL 不可用时的兜底 ---------- */
|
|
function fallback() {
|
|
if (bootEl) bootEl.remove();
|
|
document.body.classList.add("space-fallback");
|
|
// 退化为2D星空(CSS实现,见space.css)
|
|
}
|
|
|
|
if (!window.THREE) { fallback(); return; }
|
|
var testCanvas = document.createElement("canvas");
|
|
var gl = testCanvas.getContext("webgl") || testCanvas.getContext("experimental-webgl");
|
|
if (!gl) { fallback(); return; }
|
|
|
|
/* ============================================================
|
|
程序化纹理生成
|
|
============================================================ */
|
|
|
|
// 径向光晕纹理(太阳辉光/镜头光斑)
|
|
function makeGlowTexture(inner, outer, size) {
|
|
var c = document.createElement("canvas");
|
|
c.width = c.height = size;
|
|
var ctx = c.getContext("2d");
|
|
var g = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
|
|
g.addColorStop(0, inner);
|
|
g.addColorStop(0.35, inner.replace(/[\d.]+\)$/, "0.35)"));
|
|
g.addColorStop(1, outer);
|
|
ctx.fillStyle = g;
|
|
ctx.fillRect(0, 0, size, size);
|
|
var t = new THREE.CanvasTexture(c);
|
|
return t;
|
|
}
|
|
|
|
// 月球纹理: 写实环形山 + 代码字符混合
|
|
var crackPath = []; // 裂缝路径点(供粒子泄漏/光缝定位)
|
|
function makeMoonTexture() {
|
|
var W = 1024, H = 512;
|
|
var c = document.createElement("canvas");
|
|
c.width = W; c.height = H;
|
|
var ctx = c.getContext("2d");
|
|
|
|
// 基底灰
|
|
var base = ctx.createLinearGradient(0, 0, W, H);
|
|
base.addColorStop(0, "#8d8d8f");
|
|
base.addColorStop(0.5, "#7c7c80");
|
|
base.addColorStop(1, "#6e6e73");
|
|
ctx.fillStyle = base;
|
|
ctx.fillRect(0, 0, W, H);
|
|
|
|
// 月海(大块暗斑)
|
|
for (var m = 0; m < 7; m++) {
|
|
var mx = Math.random() * W, my = Math.random() * H, mr = 60 + Math.random() * 130;
|
|
var mg = ctx.createRadialGradient(mx, my, 0, mx, my, mr);
|
|
mg.addColorStop(0, "rgba(70,70,76,0.5)");
|
|
mg.addColorStop(1, "rgba(70,70,76,0)");
|
|
ctx.fillStyle = mg;
|
|
ctx.beginPath(); ctx.arc(mx, my, mr, 0, Math.PI * 2); ctx.fill();
|
|
}
|
|
|
|
// 环形山
|
|
for (var i = 0; i < 220; i++) {
|
|
var x = Math.random() * W, y = Math.random() * H;
|
|
var r = 2 + Math.random() * 18;
|
|
ctx.fillStyle = "rgba(50,50,56," + (0.15 + Math.random() * 0.25) + ")";
|
|
ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI * 2); ctx.fill();
|
|
// 亮缘
|
|
ctx.strokeStyle = "rgba(200,200,205," + (0.1 + Math.random() * 0.2) + ")";
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath(); ctx.arc(x, y, r, -0.6, 1.8); ctx.stroke();
|
|
}
|
|
|
|
// 表面噪点
|
|
for (var n = 0; n < 2600; n++) {
|
|
var nx = Math.random() * W, ny = Math.random() * H;
|
|
ctx.fillStyle = Math.random() > 0.5
|
|
? "rgba(255,255,255," + Math.random() * 0.06 + ")"
|
|
: "rgba(0,0,0," + Math.random() * 0.08 + ")";
|
|
ctx.fillRect(nx, ny, 1.5, 1.5);
|
|
}
|
|
|
|
// 代码字符纹理(远看是月面,近看全是代码)
|
|
var glyphs = "01{}[]<>/=;+*#$&%@!?~:fnrax";
|
|
ctx.textBaseline = "top";
|
|
for (var g2 = 0; g2 < 520; g2++) {
|
|
var gx = Math.random() * W, gy = Math.random() * H;
|
|
var fs = 6 + Math.random() * 8;
|
|
ctx.font = fs + "px monospace";
|
|
var greenish = Math.random() > 0.72;
|
|
ctx.fillStyle = greenish
|
|
? "rgba(90,220,150," + (0.06 + Math.random() * 0.1) + ")"
|
|
: "rgba(230,230,235," + (0.04 + Math.random() * 0.08) + ")";
|
|
ctx.fillText(glyphs[Math.floor(Math.random() * glyphs.length)], gx, gy);
|
|
}
|
|
|
|
// 裂缝(锯齿状,贯穿一侧) —— 同时记录路径
|
|
ctx.strokeStyle = "rgba(18,18,22,0.9)";
|
|
ctx.lineCap = "round";
|
|
var cx = W * 0.62, cy = H * 0.16;
|
|
ctx.lineWidth = 7;
|
|
ctx.beginPath(); ctx.moveTo(cx, cy);
|
|
crackPath.push([cx, cy]);
|
|
while (cy < H * 0.9) {
|
|
cx += (Math.random() - 0.42) * 90;
|
|
cy += 26 + Math.random() * 30;
|
|
ctx.lineTo(cx, cy);
|
|
crackPath.push([cx, cy]);
|
|
ctx.lineWidth = Math.max(2, ctx.lineWidth * 0.94);
|
|
}
|
|
ctx.stroke();
|
|
// 裂缝两侧的次级裂纹
|
|
for (var b = 0; b < 8; b++) {
|
|
var bp = crackPath[Math.floor(Math.random() * crackPath.length)];
|
|
ctx.lineWidth = 1.5;
|
|
ctx.beginPath(); ctx.moveTo(bp[0], bp[1]);
|
|
var bx = bp[0], by = bp[1];
|
|
for (var s = 0; s < 4; s++) {
|
|
bx += (Math.random() - 0.5) * 60;
|
|
by += Math.random() * 26;
|
|
ctx.lineTo(bx, by);
|
|
}
|
|
ctx.stroke();
|
|
}
|
|
|
|
var tex = new THREE.CanvasTexture(c);
|
|
tex.colorSpace = THREE.SRGBColorSpace;
|
|
return tex;
|
|
}
|
|
|
|
// 代码字符精灵纹理
|
|
function makeCharSprite(ch, color) {
|
|
var s = 48;
|
|
var c = document.createElement("canvas");
|
|
c.width = c.height = s;
|
|
var ctx = c.getContext("2d");
|
|
ctx.font = "bold 30px monospace";
|
|
ctx.textAlign = "center";
|
|
ctx.textBaseline = "middle";
|
|
ctx.shadowColor = color;
|
|
ctx.shadowBlur = 8;
|
|
ctx.fillStyle = color;
|
|
ctx.fillText(ch, s / 2, s / 2);
|
|
return new THREE.CanvasTexture(c);
|
|
}
|
|
|
|
/* ============================================================
|
|
场景搭建
|
|
============================================================ */
|
|
var scene = new THREE.Scene();
|
|
var camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 2000);
|
|
var rig = new THREE.Group(); // 相机吊舱(负责呼吸式漂移)
|
|
rig.add(camera);
|
|
scene.add(rig);
|
|
|
|
var renderer;
|
|
try {
|
|
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, powerPreference: "high-performance" });
|
|
} catch (e) { fallback(); return; }
|
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, isMobile ? 1.2 : 1.6));
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
canvasHost.appendChild(renderer.domElement);
|
|
|
|
/* ---------- 星空(两层) ---------- */
|
|
function makeStars(count, spread, size, opacity) {
|
|
var geo = new THREE.BufferGeometry();
|
|
var pos = new Float32Array(count * 3);
|
|
var col = new Float32Array(count * 3);
|
|
for (var i = 0; i < count; i++) {
|
|
pos[i * 3] = (Math.random() - 0.5) * spread;
|
|
pos[i * 3 + 1] = (Math.random() - 0.5) * spread;
|
|
pos[i * 3 + 2] = (Math.random() - 0.5) * spread;
|
|
var r = Math.random();
|
|
if (r > 0.85) { col[i * 3] = 1.0; col[i * 3 + 1] = 0.9; col[i * 3 + 2] = 0.75; } // 暖星
|
|
else if (r > 0.7) { col[i * 3] = 0.75; col[i * 3 + 1] = 0.85; col[i * 3 + 2] = 1.0; } // 蓝星
|
|
else { col[i * 3] = 1; col[i * 3 + 1] = 1; col[i * 3 + 2] = 1; }
|
|
}
|
|
geo.setAttribute("position", new THREE.BufferAttribute(pos, 3));
|
|
geo.setAttribute("color", new THREE.BufferAttribute(col, 3));
|
|
var mat = new THREE.PointsMaterial({
|
|
size: size, vertexColors: true, transparent: true, opacity: opacity,
|
|
sizeAttenuation: true, depthWrite: false
|
|
});
|
|
return new THREE.Points(geo, mat);
|
|
}
|
|
var starsFar = makeStars(isMobile ? 1500 : 3200, 900, 1.6, 0.85);
|
|
var starsNear = makeStars(isMobile ? 120 : 260, 220, 2.4, 0.6); // 近处星尘(景深)
|
|
scene.add(starsFar);
|
|
rig.add(starsNear);
|
|
|
|
/* ---------- 太阳(远景) ---------- */
|
|
var sunGroup = new THREE.Group();
|
|
var sunCore = new THREE.Mesh(
|
|
new THREE.SphereGeometry(14, 24, 24),
|
|
new THREE.MeshBasicMaterial({ color: 0xfff8e7 })
|
|
);
|
|
sunGroup.add(sunCore);
|
|
var glowTex = makeGlowTexture("rgba(255,244,214,1)", "rgba(255,244,214,0)", 256);
|
|
var sunGlow1 = new THREE.Sprite(new THREE.SpriteMaterial({ map: glowTex, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false }));
|
|
sunGlow1.scale.set(110, 110, 1);
|
|
sunGroup.add(sunGlow1);
|
|
var sunGlow2 = new THREE.Sprite(new THREE.SpriteMaterial({ map: glowTex, transparent: true, opacity: 0.5, blending: THREE.AdditiveBlending, depthWrite: false }));
|
|
sunGlow2.scale.set(220, 220, 1);
|
|
sunGroup.add(sunGlow2);
|
|
// 镜头光斑链
|
|
var flareTex = makeGlowTexture("rgba(255,236,190,0.8)", "rgba(255,236,190,0)", 128);
|
|
for (var f = 0; f < 3; f++) {
|
|
var flare = new THREE.Sprite(new THREE.SpriteMaterial({ map: flareTex, transparent: true, opacity: 0.25 - f * 0.06, blending: THREE.AdditiveBlending, depthWrite: false }));
|
|
var fs2 = [22, 10, 6][f];
|
|
flare.scale.set(fs2, fs2, 1);
|
|
flare.position.set(18 + f * 14, -6 - f * 4, 0);
|
|
sunGroup.add(flare);
|
|
}
|
|
sunGroup.position.set(-150, 70, -260);
|
|
scene.add(sunGroup);
|
|
|
|
/* ---------- 月亮(破碎) ---------- */
|
|
var moonGroup = new THREE.Group();
|
|
var MOON_R = 10;
|
|
var moonTex = makeMoonTexture();
|
|
var moonMat = new THREE.MeshStandardMaterial({
|
|
map: moonTex, bumpMap: moonTex, bumpScale: 0.35,
|
|
roughness: 0.95, metalness: 0.02
|
|
});
|
|
var moon = new THREE.Mesh(new THREE.SphereGeometry(MOON_R, 64, 64), moonMat);
|
|
moonGroup.add(moon);
|
|
|
|
// 月亮轮廓微光(大气感)
|
|
var moonHalo = new THREE.Sprite(new THREE.SpriteMaterial({
|
|
map: makeGlowTexture("rgba(190,200,220,0.5)", "rgba(190,200,220,0)", 256),
|
|
transparent: true, opacity: 0.5, blending: THREE.AdditiveBlending, depthWrite: false
|
|
}));
|
|
moonHalo.scale.set(34, 34, 1);
|
|
moonGroup.add(moonHalo);
|
|
|
|
// 碎片(3块, 不规则形状)
|
|
var fragments = [];
|
|
var fragSpecs = [
|
|
{ size: 2.6, scatter: new THREE.Vector3(9, 4.5, 3), home: new THREE.Vector3(7.6, 3.4, 2.2), spin: 0.16 },
|
|
{ size: 1.8, scatter: new THREE.Vector3(-7, -5, 4.5), home: new THREE.Vector3(-5.4, -3.9, 3.4), spin: -0.22 },
|
|
{ size: 1.3, scatter: new THREE.Vector3(4, -8, -4), home: new THREE.Vector3(3.2, -6.2, -3.1), spin: 0.3 }
|
|
];
|
|
fragSpecs.forEach(function (spec) {
|
|
var geo = new THREE.IcosahedronGeometry(spec.size, 1);
|
|
var posAttr = geo.getAttribute("position");
|
|
for (var i = 0; i < posAttr.count; i++) {
|
|
var jitter = 0.75 + Math.random() * 0.45;
|
|
posAttr.setXYZ(i, posAttr.getX(i) * jitter, posAttr.getY(i) * jitter, posAttr.getZ(i) * jitter);
|
|
}
|
|
geo.computeVertexNormals();
|
|
var mesh = new THREE.Mesh(geo, moonMat);
|
|
mesh.position.copy(spec.scatter);
|
|
mesh.rotation.set(Math.random() * 3, Math.random() * 3, Math.random() * 3);
|
|
moonGroup.add(mesh);
|
|
fragments.push({ mesh: mesh, spec: spec, phase: Math.random() * Math.PI * 2 });
|
|
});
|
|
|
|
// 修复光缝(绿色能量缝,随修复进度亮起)
|
|
var seams = [];
|
|
var seamTex = makeGlowTexture("rgba(77,255,166,1)", "rgba(77,255,166,0)", 64);
|
|
fragSpecs.forEach(function (spec) {
|
|
var seam = new THREE.Sprite(new THREE.SpriteMaterial({
|
|
map: seamTex, color: 0x4dffa6, transparent: true, opacity: 0,
|
|
blending: THREE.AdditiveBlending, depthWrite: false
|
|
}));
|
|
seam.scale.set(spec.size * 3.2, spec.size * 1.2, 1);
|
|
seam.position.copy(spec.home).multiplyScalar(0.92);
|
|
moonGroup.add(seam);
|
|
seams.push(seam);
|
|
});
|
|
|
|
moonGroup.position.set(6, 1, -26);
|
|
scene.add(moonGroup);
|
|
|
|
/* ---------- 代码泄漏粒子 ---------- */
|
|
var codeChars = "01{}<>/=;#*$fx";
|
|
var leakSprites = [];
|
|
var LEAK_COUNT = isMobile ? 22 : 46;
|
|
var charTexCache = {};
|
|
function charTex(ch) {
|
|
if (!charTexCache[ch]) charTexCache[ch] = makeCharSprite(ch, "#4dffa6");
|
|
return charTexCache[ch];
|
|
}
|
|
for (var li = 0; li < LEAK_COUNT; li++) {
|
|
var sp = new THREE.Sprite(new THREE.SpriteMaterial({
|
|
map: charTex(codeChars[Math.floor(Math.random() * codeChars.length)]),
|
|
transparent: true, opacity: 0, depthWrite: false, blending: THREE.AdditiveBlending
|
|
}));
|
|
var sc = 0.5 + Math.random() * 0.7;
|
|
sp.scale.set(sc, sc, 1);
|
|
// 从裂缝附近发射
|
|
var emit = new THREE.Vector3(
|
|
moonGroup.position.x + 3 + Math.random() * 6,
|
|
moonGroup.position.y - 4 + Math.random() * 8,
|
|
moonGroup.position.z + 2 + Math.random() * 4
|
|
);
|
|
sp.position.copy(emit);
|
|
scene.add(sp);
|
|
leakSprites.push({
|
|
sp: sp, life: Math.random(), speed: 0.06 + Math.random() * 0.1,
|
|
drift: new THREE.Vector3((Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 0.3),
|
|
origin: emit
|
|
});
|
|
}
|
|
|
|
/* ---------- 灯光 ---------- */
|
|
var sunLight = new THREE.DirectionalLight(0xfff3dd, 2.2);
|
|
sunLight.position.set(-150, 70, -200);
|
|
scene.add(sunLight);
|
|
scene.add(new THREE.AmbientLight(0x334455, 0.5));
|
|
var rimLight = new THREE.DirectionalLight(0x8899ff, 0.4); // 背面冷光勾勒
|
|
rimLight.position.set(80, -40, 60);
|
|
scene.add(rimLight);
|
|
|
|
/* ============================================================
|
|
相机编排: 呼吸漂移 + 鼠标惯性 + 滚动驱动
|
|
============================================================ */
|
|
var mouseX = 0, mouseY = 0;
|
|
if (!isMobile) {
|
|
document.addEventListener("mousemove", function (e) {
|
|
mouseX = (e.clientX / window.innerWidth - 0.5) * 2;
|
|
mouseY = (e.clientY / window.innerHeight - 0.5) * 2;
|
|
});
|
|
}
|
|
|
|
function scrollProgress() {
|
|
var h = document.documentElement.scrollHeight - window.innerHeight;
|
|
return h > 0 ? Math.min(Math.max(window.scrollY / h, 0), 1) : 0;
|
|
}
|
|
|
|
function easeInOut(t) { return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; }
|
|
function lerp(a, b, t) { return a + (b - a) * t; }
|
|
|
|
var isJourney = window.SPACE_MODE === "journey";
|
|
var warpT = reducedMotion ? 1 : 0; // 0→1 warp入场
|
|
var clock = new THREE.Clock();
|
|
var bootDone = false;
|
|
|
|
/* ============================================================
|
|
BOOT 加载序列 → warp 穿越入场
|
|
============================================================ */
|
|
var bootLines = [
|
|
{ t: "> 接入深空观测网络 ………… ", ok: "[OK]" },
|
|
{ t: "> 望远镜阵列对焦 …………… ", ok: "[OK]" },
|
|
{ t: "> 扫描目标: MOON.SYS ……… ", ok: "[FATAL]" },
|
|
{ t: "> 检测到结构性碎裂 ………… ", ok: "[3 FRAGS]" },
|
|
{ t: "> 启动修复协议 ……………… ", ok: "[GO]" }
|
|
];
|
|
var bootBody = document.getElementById("spaceBootBody");
|
|
var bootBar = document.getElementById("spaceBootBar");
|
|
|
|
function runBoot() {
|
|
if (!bootEl || reducedMotion) { finishBoot(); return; }
|
|
var i = 0;
|
|
var total = bootLines.length;
|
|
function next() {
|
|
if (i < total) {
|
|
var line = document.createElement("div");
|
|
line.className = "terminal-line";
|
|
var isFatal = bootLines[i].ok.indexOf("FATAL") >= 0 || bootLines[i].ok.indexOf("FRAG") >= 0;
|
|
line.innerHTML = '<span class="terminal-prompt">' + bootLines[i].t + '</span><span class="terminal-text ' + (isFatal ? "log-error" : "ok") + '">' + bootLines[i].ok + "</span>";
|
|
if (bootBody) bootBody.appendChild(line);
|
|
if (bootBar) bootBar.style.width = ((i + 1) / total * 100) + "%";
|
|
i++;
|
|
setTimeout(next, 340);
|
|
} else {
|
|
setTimeout(finishBoot, 420);
|
|
}
|
|
}
|
|
next();
|
|
}
|
|
|
|
function finishBoot() {
|
|
if (bootEl) {
|
|
bootEl.classList.add("hide");
|
|
setTimeout(function () { if (bootEl.parentNode) bootEl.parentNode.removeChild(bootEl); }, 900);
|
|
}
|
|
bootDone = true;
|
|
document.body.classList.add("space-ready");
|
|
}
|
|
runBoot();
|
|
// 保险: 4.5s内必须入场
|
|
setTimeout(finishBoot, 4500);
|
|
|
|
/* ============================================================
|
|
主循环
|
|
============================================================ */
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
var t = clock.getElapsedTime();
|
|
var p = isJourney ? scrollProgress() : 0;
|
|
var ep = easeInOut(p);
|
|
|
|
// warp入场: 相机从远处冲刺到工位
|
|
if (warpT < 1) {
|
|
warpT = Math.min(1, warpT + 0.008);
|
|
var w = 1 - Math.pow(1 - warpT, 3);
|
|
camera.position.z = lerp(220, isJourney ? 46 : 42, w);
|
|
starsFar.rotation.z += 0.002 * (1 - warpT);
|
|
} else if (isJourney) {
|
|
// 滚动驱动: 接近月亮
|
|
camera.position.z = lerp(46, 24, ep);
|
|
camera.position.y = lerp(0, 3, ep);
|
|
} else {
|
|
camera.position.z = 42;
|
|
}
|
|
|
|
// 呼吸式失重漂移
|
|
rig.position.x = Math.sin(t * 0.24) * 0.55;
|
|
rig.position.y = Math.cos(t * 0.19) * 0.45;
|
|
rig.rotation.z = Math.sin(t * 0.1) * 0.008;
|
|
|
|
// 鼠标惯性跟随(像在太空里转头)
|
|
camera.rotation.y += ((-mouseX * 0.09) - camera.rotation.y) * 0.045;
|
|
camera.rotation.x += ((-mouseY * 0.06) - camera.rotation.x) * 0.045;
|
|
|
|
// 月亮自转 + 月组轻微摆动
|
|
moon.rotation.y += 0.0009;
|
|
moonGroup.position.y = 1 + Math.sin(t * 0.35) * 0.35;
|
|
moonGroup.rotation.z = Math.sin(t * 0.12) * 0.02;
|
|
|
|
// 修复进度: journey模式随滚动到85%停住 / orbit模式固定0.3
|
|
var repair = isJourney ? Math.min(Math.max((p - 0.42) / 0.46, 0), 1) * 0.85 : 0.3;
|
|
var rt = repair / 0.85; // 0→1
|
|
|
|
fragments.forEach(function (f) {
|
|
var target = f.spec.scatter.clone().lerp(f.spec.home, rt * rt);
|
|
// 悬浮: 在目标点附近小幅浮动
|
|
f.mesh.position.set(
|
|
target.x + Math.sin(t * 0.5 + f.phase) * 0.35 * (1 - rt),
|
|
target.y + Math.cos(t * 0.42 + f.phase) * 0.3 * (1 - rt),
|
|
target.z + Math.sin(t * 0.31 + f.phase * 2) * 0.3 * (1 - rt)
|
|
);
|
|
f.mesh.rotation.x += 0.0016 * f.spec.spin * (1 - rt * 0.8);
|
|
f.mesh.rotation.y += 0.0022 * f.spec.spin * (1 - rt * 0.8);
|
|
});
|
|
|
|
// 光缝亮起
|
|
seams.forEach(function (s, i) {
|
|
s.material.opacity = rt * 0.85 + Math.sin(t * 2 + i) * 0.06 * rt;
|
|
});
|
|
|
|
// 代码泄漏(修复越少漏得越多)
|
|
var leakRate = 1 - rt * 0.7;
|
|
leakSprites.forEach(function (L) {
|
|
L.life += L.speed * 0.016 * 60 * 0.016;
|
|
if (L.life > 1) {
|
|
L.life = 0;
|
|
L.sp.position.copy(L.origin);
|
|
L.sp.material.map = charTex(codeChars[Math.floor(Math.random() * codeChars.length)]);
|
|
}
|
|
L.sp.position.x += L.drift.x * 0.016;
|
|
L.sp.position.y += L.drift.y * 0.016;
|
|
L.sp.position.z += L.drift.z * 0.016;
|
|
L.sp.material.opacity = Math.sin(L.life * Math.PI) * 0.75 * leakRate;
|
|
});
|
|
|
|
// 太阳呼吸
|
|
sunGlow1.material.opacity = 0.85 + Math.sin(t * 0.8) * 0.1;
|
|
|
|
// 星尘缓流
|
|
starsNear.rotation.y += 0.0002;
|
|
starsFar.rotation.y += 0.00004;
|
|
|
|
renderer.render(scene, camera);
|
|
}
|
|
animate();
|
|
|
|
/* ---------- 自适应 ---------- */
|
|
window.addEventListener("resize", function () {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
})();
|