Initial commit: RHINE LAB · ANALYSIS OS:三维界面与动效实验(本仓库不含个人归档索引数据)

This commit is contained in:
WpyQwq
2026-09-19 13:04:56 +08:00
commit 3f422218a5
1098 changed files with 39410 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>外壳 · 刻线与解密过渡</title>
<style>
*{box-sizing:border-box}body{margin:0;background:#eae5e1;color:#292c28;font:14px Arial,"Microsoft YaHei",sans-serif}main{max-width:1700px;margin:auto;padding:28px}h1{font-size:23px;margin:0 0 12px}p{color:#676962;line-height:1.7}nav{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin:24px 0}select,button,a{padding:9px 12px;border:1px solid #b7b9b0;background:transparent;color:inherit;font:inherit}a{text-decoration:none}label{display:flex;gap:10px;align-items:center}.views{display:grid;grid-template-columns:1fr 1fr;gap:20px}.view{height:440px;position:relative;background:#e1e0d9}canvas{width:100%!important;height:100%!important}h2{font-size:15px;font-weight:500}pre{font:12px monospace;white-space:pre-wrap;color:#666}button[aria-pressed=true]{background:#353b30;color:#f1f0e8}@media(max-width:800px){.views{grid-template-columns:1fr}.view{height:330px}}</style>
<main><h1>外壳 · 刻线与解密过渡</h1><p>检查顶边嵌片、两处螺丝及盖板内刻线。播放或拖动进度,观察清晰区域从顶部向下推进。</p>
<nav><label>显示<select id="display"><option value="clear">装配 · 清晰盖板</option><option value="frosted">装配 · 磨砂盖板</option></select></label><button data-angle="oblique" aria-pressed="true">斜向观察</button><button data-angle="front">正面</button><button data-angle="edge">低角度</button><label>缩放<select id="zoom"><option value="1">完整外壳</option><option value="1.6">顶部 1.6×</option><option value="2.2">右上螺丝 2.2×</option></select></label><a href="/?scene=detail" target="_blank">打开详情体验 ↗</a><label>清晰进度<input id="progress" type="range" min="0" max="1" step="0.01" value="1"></label><button id="play">播放变清晰</button></nav>
<section class="views"><article><h2>打磨前</h2><div class="view" id="before"></div></article><article><h2>外壳打磨版</h2><div class="view" id="after"></div></article></section><pre id="status">正在加载…</pre></main>
<script type="module">
import * as THREE from 'three';
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
import {ArchiveScene} from '/src/scene.ts';
const $=q=>document.querySelector(q),scenes=[new ArchiveScene($('#before')),new ArchiveScene($('#after'))],groups=[];
let angle='oblique',ready=false;const grids=[];
scenes.forEach(s=>s.composer.render=()=>{});
async function asset(s,path){
const raw=(await new GLTFLoader().loadAsync(path)).scene;raw.updateMatrixWorld(true);const group=new THREE.Group();
raw.traverse(o=>{if(!o.isMesh)return;const m=new THREE.Mesh(o.geometry.clone().applyMatrix4(o.matrixWorld),o.material);m.userData={assemblyPart:o.userData.assemblyPart,surface:o.material.name.replace(/\.\d+$/,'')};group.add(m)});
s.appearance.prepare(group);s.appearance.apply(group,1);s.appearance.setClarity(group,1);const label=s.model.children.find(o=>o.isMesh&&!o.userData.surface)?.clone();if(label){label.material=label.material.clone();label.material.opacity=1;group.add(label)}group.position.y=-1.85;s.scene.add(group);return group;
}
function draw(){
if(!ready)return;const mode=$('#display').value,zoom=Number($('#zoom').value);
scenes.forEach((s,i)=>{
grids[i].visible=mode==='interior';const g=groups[i];for(const mesh of g.children)mesh.visible=mode!=='interior'||['optical-core','optical-lenses'].includes(mesh.userData.assemblyPart);
s.appearance.setClarity(g,Number($('#progress').value));
const points={front:[.2,.15,7.6],oblique:[2.6,4.5,6.6],edge:[3.8,7.5,3.0]};
const target=new THREE.Vector3(zoom>2?2.1:zoom>1?0:.12,zoom>1?1.4:.1,0);
const dir=new THREE.Vector3(...points[angle]);s.camera.position.copy(target).add(dir.multiplyScalar((mode==='interior'?.66:1.03)/zoom));s.camera.near=.1;s.camera.far=120;s.camera.fov=34;
s.camera.aspect=s.container.clientWidth/s.container.clientHeight;s.camera.lookAt(target);s.camera.updateProjectionMatrix();s.camera.updateMatrixWorld(true);
s.renderer.setPixelRatio(Math.min(devicePixelRatio,2));s.renderer.setSize(s.container.clientWidth,s.container.clientHeight,false);
s.renderer.render(s.scene,s.camera);
});
$('#status').textContent='清晰进度 '+Math.round(Number($('#progress').value)*100)+'% · 两份模型使用相同的新版盖板过渡';
}
try{
await Promise.all(scenes.map((s,i)=>s.load(i?'/assets/archive-cassette.glb':'/reference/shell-study/assembly-before.glb')));
for(const s of scenes){s.scene.fog=null;for(const child of s.scene.children)if(child.isMesh||child.isGroup)child.visible=false;}
groups.push(...await Promise.all(scenes.map((s,i)=>asset(s,i?'/assets/archive-assembly.glb':'/reference/shell-study/assembly-before.glb'))));
for(const s of scenes){
const grid=new THREE.GridHelper(8,32,0xaaa99d,0xbab9ae);grid.rotation.x=Math.PI/2;grid.position.z=-.065;s.scene.add(grid);grids.push(grid);
}
ready=true;draw();
}catch(e){$('#status').textContent=String(e);console.error(e)}
document.querySelectorAll('[data-angle]').forEach(b=>b.onclick=()=>{angle=b.dataset.angle;document.querySelectorAll('[data-angle]').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));draw()});
let queued=false;const queueDraw=()=>{if(queued)return;queued=true;requestAnimationFrame(()=>{queued=false;draw()})};
$('#progress').oninput=()=>{$('#display').value=Number($('#progress').value)===0?'frosted':'clear';queueDraw()};$('#display').onchange=()=>{$('#progress').value=$('#display').value==='frosted'?'0':'1';queueDraw()};let playId=0;$('#play').onclick=()=>{cancelAnimationFrame(playId);const start=performance.now();const tick=now=>{$('#progress').value=Math.min(1,(now-start)/2200);draw();if(now-start<2200)playId=requestAnimationFrame(tick)};playId=requestAnimationFrame(tick)};$('#zoom').onchange=queueDraw;addEventListener('resize',queueDraw);
</script></html>