- Karaktere tek tikla: o kisinin bir sonraki repligine atlar ve oynatir (oncesinde cift tik sadece ilk sozune gidiyordu) - Sag ustte kucuk #blokNo gostergesi (hangi replikte oldugunu gormek icin) - Kaan/Marissa koltuklari hafif ice alindi (-1.6/1.6 -> -1.36/1.36) - Ipucu metni guncellendi Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
323 lines
19 KiB
HTML
323 lines
19 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="tr">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Toplantı 3D — Orijinal / Türkçe</title>
|
||
<style>
|
||
:root{ --bg:#0d1117; --panel:#161b22cc; --border:#30363d; --txt:#e6edf3; --mut:#8b949e; --accent:#2f81f7; }
|
||
*{box-sizing:border-box}
|
||
html,body{margin:0;height:100%;background:var(--bg);color:var(--txt);font-family:"Segoe UI",system-ui,sans-serif;overflow:hidden}
|
||
#scene{position:fixed;inset:0}
|
||
#topbar{position:fixed;top:0;left:0;right:0;height:52px;display:flex;align-items:center;gap:14px;
|
||
padding:0 16px;background:linear-gradient(#0d1117ee,#0d111700);z-index:10;pointer-events:none}
|
||
#topbar h1{font-size:15px;margin:0;font-weight:600;pointer-events:auto}
|
||
#topbar .sub{font-size:12px;color:var(--mut);font-weight:400}
|
||
.spacer{flex:1}
|
||
.toggle{pointer-events:auto;display:flex;background:#21262d;border:1px solid var(--border);border-radius:20px;overflow:hidden}
|
||
.toggle button{background:none;border:0;color:var(--mut);padding:7px 15px;font-size:13px;cursor:pointer;font-weight:600}
|
||
.toggle button.on{background:var(--accent);color:#fff}
|
||
.toggle button.on.trm{background:#8957e5}
|
||
#legend{position:fixed;top:60px;left:12px;z-index:10;background:var(--panel);border:1px solid var(--border);
|
||
border-radius:10px;padding:10px 12px;font-size:11.5px;backdrop-filter:blur(6px);max-width:220px}
|
||
#legend .row{display:flex;align-items:center;gap:7px;margin-top:5px}
|
||
#legend .dot{width:11px;height:11px;border-radius:50%;flex:none}
|
||
#subtitle{position:fixed;left:50%;transform:translateX(-50%);bottom:90px;width:min(760px,94vw);z-index:10;
|
||
background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:6px 14px;backdrop-filter:blur(8px);
|
||
text-align:center;transition:opacity .2s}
|
||
#subtitle .who{font-weight:700;font-size:12px;margin-bottom:1px}
|
||
#subtitle .orig{font-size:12.5px;line-height:1.24}
|
||
#subtitle .tr{font-size:12.5px;line-height:1.24;color:var(--mut);margin-top:1px}
|
||
#subtitle.trmode .orig{color:var(--mut);font-size:12.5px}
|
||
#subtitle.trmode .tr{color:var(--txt)}
|
||
.badge{display:inline-block;font-size:10px;background:#3d2a00;color:#e0b400;border:1px solid #6b4a00;
|
||
border-radius:6px;padding:1px 6px;margin-left:6px;vertical-align:middle}
|
||
#controls{position:fixed;left:0;right:0;bottom:0;z-index:10;background:linear-gradient(#0d111700,#0d1117ee 42%);padding:12px 18px 14px}
|
||
#tl{width:100%;height:34px;display:block;cursor:pointer;border-radius:6px}
|
||
#bar{display:flex;align-items:center;gap:12px;margin-top:8px}
|
||
.btn{background:#21262d;border:1px solid var(--border);color:var(--txt);width:40px;height:40px;border-radius:50%;
|
||
font-size:15px;cursor:pointer;display:flex;align-items:center;justify-content:center}
|
||
.btn:hover{border-color:var(--accent)}
|
||
.btn.play{width:46px;height:46px;font-size:17px;background:var(--accent);border-color:var(--accent)}
|
||
#time{font-variant-numeric:tabular-nums;font-size:13px;color:var(--mut);min-width:120px}
|
||
#hint{font-size:11px;color:var(--mut)}
|
||
@media(max-width:640px){
|
||
#subtitle{bottom:138px}
|
||
#hint{display:none}
|
||
#bar{flex-wrap:wrap}
|
||
#topbar h1{font-size:13px}
|
||
#topbar .sub{display:none}
|
||
.toggle button{padding:6px 10px;font-size:12px}
|
||
#legend{max-width:150px;font-size:10px;padding:7px 9px;top:56px}
|
||
#legend b{font-size:10.5px}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="scene"></div>
|
||
<div id="topbar">
|
||
<h1>Toplantı — 3D Oda <span class="sub">· uzlaşma görüşmesi (düzenlenmiş kayıt)</span></h1>
|
||
<div class="spacer"></div>
|
||
<div class="toggle" id="langToggle">
|
||
<button data-m="orig" class="on">🔊 Orijinal</button>
|
||
<button data-m="tr">🇹🇷 Türkçe</button>
|
||
</div>
|
||
<span id="lineId" style="pointer-events:auto;font-size:9px;color:#484f58;margin-left:8px">#—</span>
|
||
</div>
|
||
<div id="legend"><b>Masadaki kişiler</b><div id="legendRows"></div></div>
|
||
<div id="subtitle">
|
||
<div class="who" id="subWho">—</div>
|
||
<div class="orig" id="subOrig">Başlamak için ▶ tuşuna bas</div>
|
||
<div class="tr" id="subTr"></div>
|
||
</div>
|
||
<div id="controls">
|
||
<canvas id="tl"></canvas>
|
||
<div id="bar">
|
||
<button class="btn play" id="play">▶</button>
|
||
<button class="btn" id="prev" title="Önceki konuşma">⏮</button>
|
||
<button class="btn" id="next" title="Sonraki konuşma">⏭</button>
|
||
<span id="time">00:00 / 00:00</span>
|
||
<div class="spacer"></div>
|
||
<span id="hint">Zaman çizelgesine tıkla/sürükle · fareyle sahneyi döndür · kişiye tıkla → sıradaki sözü</span>
|
||
</div>
|
||
</div>
|
||
<div id="loadErr" style="display:none;position:fixed;inset:0;z-index:999;background:#0d1117;color:#e6edf3;
|
||
font-family:'Segoe UI',system-ui,sans-serif;align-items:center;justify-content:center;text-align:center;padding:24px">
|
||
<div>
|
||
<div style="font-size:15px;font-weight:600;margin-bottom:8px">⚠️ Bir dosya yüklenemedi</div>
|
||
<div id="loadErrMsg" style="font-size:13px;color:#8b949e"></div>
|
||
<div style="font-size:12px;color:#8b949e;margin-top:10px">Sayfayı yeniden yüklemeyi deneyin veya bir yerel sunucu üzerinden açın (file:// kısıtlamaları olabilir).</div>
|
||
</div>
|
||
</div>
|
||
<script>
|
||
function showLoadErr(msg){
|
||
const box=document.getElementById('loadErr'), m=document.getElementById('loadErrMsg');
|
||
if(m)m.textContent=msg; if(box){box.style.display='flex';}
|
||
}
|
||
</script>
|
||
<audio id="ao" src="toplanti_orijinal.mp3?v=20260704d" preload="metadata" onerror="showLoadErr('toplanti_orijinal.mp3 yüklenemedi')"></audio>
|
||
<audio id="at" preload="metadata" onerror="window.TR_AUDIO_FAILED=true"></audio>
|
||
|
||
<script src="three.min.js?v=20260704d" onerror="showLoadErr('three.min.js (3D motoru) yüklenemedi')"></script>
|
||
<script src="OrbitControls.js?v=20260704d" onerror="showLoadErr('OrbitControls.js (kamera kontrolü) yüklenemedi')"></script>
|
||
<script src="data.js?v=20260704d" onerror="showLoadErr('data.js (toplantı verisi) yüklenemedi')"></script>
|
||
<script>
|
||
if(typeof MEET==='undefined'){ showLoadErr('data.js yüklenemedi veya bozuk (MEET tanımsız)'); throw new Error('MEET tanımsız — data.js yüklenemedi'); }
|
||
const ROLES=MEET.roles, TL=MEET.TL;
|
||
const POS3D={
|
||
"Kaan":{x:-1.36,z:2.3,stand:false}, "Marissa":{x:1.36,z:2.3,stand:false},
|
||
"Dolandırıcı (D-)":{x:-1.6,z:-2.3,stand:false}, "Uzlaşmacı (U)":{x:0.4,z:-2.3,stand:false},
|
||
"Yaşlı adam (V)":{x:-3.7,z:0,stand:false}, "Lider Uzlaşmacı (L.U)":{x:3.7,z:0,stand:false},
|
||
"Kayınvalide":{x:3.3,z:-2.6,stand:true}
|
||
};
|
||
const SHORT={"Kaan":"Kaan","Marissa":"Marissa","Dolandırıcı (D-)":"Scammer Litoy","Uzlaşmacı (U)":"Kuya 2",
|
||
"Yaşlı adam (V)":"Manong","Lider Uzlaşmacı (L.U)":"Kuya 1","Kayınvalide":"Daisy ⬆"};
|
||
|
||
function roundRect(g,x,y,w,h,r){g.beginPath();g.moveTo(x+r,y);g.arcTo(x+w,y,x+w,y+h,r);g.arcTo(x+w,y+h,x,y+h,r);g.arcTo(x,y+h,x,y,r);g.arcTo(x,y,x+w,y,r);g.closePath();}
|
||
function makeLabel(text,color,scale){ scale=scale||1;
|
||
const c=document.createElement('canvas'); c.width=256; c.height=64; const g=c.getContext('2d');
|
||
g.fillStyle="rgba(13,17,23,0.85)"; roundRect(g,4,10,248,44,10); g.fill();
|
||
g.strokeStyle=color; g.lineWidth=2; roundRect(g,4,10,248,44,10); g.stroke();
|
||
g.fillStyle=color; g.font="bold 24px Segoe UI"; g.textAlign="center"; g.textBaseline="middle"; g.fillText(text,128,33);
|
||
const spr=new THREE.Sprite(new THREE.SpriteMaterial({map:new THREE.CanvasTexture(c),transparent:true,depthTest:false}));
|
||
spr.scale.set(1.9*scale,0.48*scale,1); return spr;
|
||
}
|
||
|
||
const scene=new THREE.Scene();
|
||
scene.background=new THREE.Color(0x0d1117);
|
||
scene.fog=new THREE.Fog(0x0d1117,16,34);
|
||
const camera=new THREE.PerspectiveCamera(48,innerWidth/innerHeight,0.1,100);
|
||
function fitCamera(){
|
||
const narrow=innerWidth<700;
|
||
camera.position.set(0, narrow?8.6:6.2, narrow?14.5:9.6);
|
||
camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix();
|
||
}
|
||
fitCamera();
|
||
const renderer=new THREE.WebGLRenderer({antialias:true});
|
||
renderer.setSize(innerWidth,innerHeight); renderer.setPixelRatio(Math.min(devicePixelRatio,2));
|
||
document.getElementById('scene').appendChild(renderer.domElement);
|
||
const controls=new THREE.OrbitControls(camera,renderer.domElement);
|
||
controls.enableDamping=true; controls.dampingFactor=0.08; controls.target.set(0,1,0);
|
||
controls.maxPolarAngle=Math.PI*0.49; controls.minDistance=5; controls.maxDistance=22;
|
||
|
||
scene.add(new THREE.AmbientLight(0xffffff,0.55));
|
||
const key=new THREE.DirectionalLight(0xffffff,0.9); key.position.set(6,12,8); scene.add(key);
|
||
const fill=new THREE.DirectionalLight(0x88aaff,0.35); fill.position.set(-8,6,-6); scene.add(fill);
|
||
|
||
const floor=new THREE.Mesh(new THREE.PlaneGeometry(40,40),new THREE.MeshStandardMaterial({color:0x1c2230,roughness:0.95}));
|
||
floor.rotation.x=-Math.PI/2; scene.add(floor);
|
||
const rug=new THREE.Mesh(new THREE.CircleGeometry(7.5,48),new THREE.MeshStandardMaterial({color:0x232a3a,roughness:1}));
|
||
rug.rotation.x=-Math.PI/2; rug.position.y=0.01; scene.add(rug);
|
||
|
||
const TABLE_H=1.0;
|
||
const tableTop=new THREE.Mesh(new THREE.BoxGeometry(6.4,0.16,3.2),new THREE.MeshStandardMaterial({color:0x6b4a2b,roughness:0.6}));
|
||
tableTop.position.y=TABLE_H; scene.add(tableTop);
|
||
[[2.9,1.4],[-2.9,1.4],[2.9,-1.4],[-2.9,-1.4]].forEach(function(p){
|
||
const leg=new THREE.Mesh(new THREE.CylinderGeometry(0.11,0.11,TABLE_H,12),new THREE.MeshStandardMaterial({color:0x4a3220}));
|
||
leg.position.set(p[0],TABLE_H/2,p[1]); scene.add(leg);
|
||
});
|
||
const rec=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.12,0.28),new THREE.MeshStandardMaterial({color:0xd21f1f,emissive:0x330000,roughness:0.4}));
|
||
rec.position.set(0,TABLE_H+0.14,0); scene.add(rec);
|
||
const recLbl=makeLabel("🔴 KAYIT","#ff6666",0.55); recLbl.position.set(0,TABLE_H+0.55,0); scene.add(recLbl);
|
||
|
||
const SKIN=0xe8b98c, chars={};
|
||
function makeChar(role){
|
||
const p=POS3D[role], col=new THREE.Color(ROLES[role].renk);
|
||
const grp=new THREE.Group();
|
||
const cloth=new THREE.MeshStandardMaterial({color:col,roughness:0.7});
|
||
const skin=new THREE.MeshStandardMaterial({color:SKIN,roughness:0.6});
|
||
const hair=new THREE.MeshStandardMaterial({color:0x2a2018,roughness:0.9});
|
||
let headY;
|
||
if(p.stand){
|
||
const legL=new THREE.Mesh(new THREE.CylinderGeometry(0.1,0.1,0.85,10),cloth); legL.position.set(-0.13,0.42,0);
|
||
const legR=legL.clone(); legR.position.x=0.13; grp.add(legL,legR);
|
||
const torso=new THREE.Mesh(new THREE.CylinderGeometry(0.22,0.32,0.85,14),cloth); torso.position.y=1.28; grp.add(torso);
|
||
const head=new THREE.Mesh(new THREE.SphereGeometry(0.19,20,20),skin); head.position.y=1.86; grp.add(head);
|
||
const h=new THREE.Mesh(new THREE.SphereGeometry(0.205,18,18,0,Math.PI*2,0,Math.PI*0.62),hair); h.position.y=1.9; grp.add(h);
|
||
headY=1.86;
|
||
} else {
|
||
const seat=new THREE.Mesh(new THREE.BoxGeometry(0.56,0.1,0.56),new THREE.MeshStandardMaterial({color:0x394150,roughness:0.8}));
|
||
seat.position.set(0,0.5,-0.08); grp.add(seat);
|
||
const back=new THREE.Mesh(new THREE.BoxGeometry(0.56,0.62,0.08),new THREE.MeshStandardMaterial({color:0x394150,roughness:0.8}));
|
||
back.position.set(0,0.8,-0.34); grp.add(back);
|
||
const thigh=new THREE.Mesh(new THREE.BoxGeometry(0.34,0.14,0.42),cloth); thigh.position.set(0,0.6,0.14); grp.add(thigh);
|
||
const shinL=new THREE.Mesh(new THREE.CylinderGeometry(0.08,0.08,0.5,10),cloth); shinL.position.set(-0.12,0.3,0.32); grp.add(shinL);
|
||
const shinR=shinL.clone(); shinR.position.x=0.12; grp.add(shinR);
|
||
const torso=new THREE.Mesh(new THREE.CylinderGeometry(0.2,0.3,0.62,14),cloth); torso.position.y=0.98; grp.add(torso);
|
||
const head=new THREE.Mesh(new THREE.SphereGeometry(0.185,20,20),skin); head.position.y=1.44; grp.add(head);
|
||
const h=new THREE.Mesh(new THREE.SphereGeometry(0.2,18,18,0,Math.PI*2,0,Math.PI*0.6),hair); h.position.y=1.48; grp.add(h);
|
||
headY=1.44;
|
||
}
|
||
const ring=new THREE.Mesh(new THREE.TorusGeometry(0.55,0.06,10,32),new THREE.MeshStandardMaterial({color:col,emissive:col,emissiveIntensity:1.4}));
|
||
ring.rotation.x=-Math.PI/2; ring.position.y=0.04; ring.visible=false; grp.add(ring);
|
||
// konuşan kişinin üstünde ampul + ışık
|
||
const bulb=new THREE.Mesh(new THREE.SphereGeometry(0.1,16,16),new THREE.MeshStandardMaterial({color:0xfff6c0,emissive:0xffca28,emissiveIntensity:2.2}));
|
||
bulb.position.y=headY+1.05; bulb.visible=false; grp.add(bulb);
|
||
const halo=new THREE.Mesh(new THREE.SphereGeometry(0.17,16,16),new THREE.MeshBasicMaterial({color:0xffd23a,transparent:true,opacity:0.25}));
|
||
halo.position.y=headY+1.05; halo.visible=false; grp.add(halo);
|
||
const blight=new THREE.PointLight(0xffd23a,0,4,2); blight.position.set(0,headY+1.0,0); grp.add(blight);
|
||
const lbl=makeLabel(SHORT[role],"#"+col.getHexString()); lbl.position.y=headY+0.55; grp.add(lbl);
|
||
grp.position.set(p.x,0,p.z); grp.rotation.y=Math.atan2(-p.x,-p.z);
|
||
scene.add(grp); chars[role]={grp:grp,ring:ring,bulb:bulb,halo:halo,blight:blight,active:false};
|
||
}
|
||
Object.keys(POS3D).forEach(makeChar);
|
||
|
||
// ---- audio + timeline ----
|
||
const ao=document.getElementById('ao'), at=document.getElementById('at');
|
||
let mode='orig';
|
||
const TL_TR=MEET.TL_tr||null;
|
||
if(MEET.trAudio){ at.src=MEET.trAudio+'?v=20260704d'; }
|
||
function TR_READY(){ return !!MEET.trAudio && !!TL_TR && !window.TR_AUDIO_FAILED; }
|
||
function A(){ return (mode==='tr'&&TR_READY())?at:ao; }
|
||
function curTL(){ return (mode==='tr'&&TR_READY())?TL_TR:TL; }
|
||
|
||
const lr=document.getElementById('legendRows');
|
||
Object.keys(POS3D).forEach(function(r){
|
||
const d=document.createElement('div'); d.className='row';
|
||
d.innerHTML='<span class="dot" style="background:'+ROLES[r].renk+'"></span>'+SHORT[r]+(POS3D[r].stand?' <span style="color:#8b949e">(ayakta)</span>':'');
|
||
lr.appendChild(d);
|
||
});
|
||
|
||
const tl=document.getElementById('tl'), tg=tl.getContext('2d');
|
||
function curDur(){ const d=A().duration, T=curTL(); return (Number.isFinite(d)&&d>0) ? d : T[T.length-1].e; }
|
||
function fitTL(){ tl.width=tl.clientWidth*devicePixelRatio; tl.height=34*devicePixelRatio; tg.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0); drawTL(); }
|
||
function drawTL(){
|
||
const T=curTL(), dur=curDur(), W=tl.clientWidth, H=34;
|
||
tg.clearRect(0,0,W,H);
|
||
tg.fillStyle="#21262d"; roundRect(tg,0,11,W,12,6); tg.fill();
|
||
tg.globalAlpha=0.85;
|
||
T.forEach(function(b){ const x=b.t/dur*W, w=Math.max(1,(b.e-b.t)/dur*W); tg.fillStyle=(ROLES[b.rol]&&ROLES[b.rol].renk)||"#888"; tg.fillRect(x,11,w,12); });
|
||
tg.globalAlpha=1;
|
||
const ph=(A().currentTime/dur)*W; tg.fillStyle="#fff"; tg.fillRect(ph-1,6,2,22); tg.beginPath(); tg.arc(ph,6,4,0,7); tg.fill();
|
||
}
|
||
function seekAtClientX(clientX){ const r=tl.getBoundingClientRect(); const x=(clientX-r.left)/r.width; const a=A(); a.currentTime=Math.max(0,Math.min(1,x))*curDur(); update(); }
|
||
let drag=false;
|
||
tl.addEventListener('mousedown',function(e){drag=true;seekAtClientX(e.clientX);});
|
||
addEventListener('mousemove',function(e){if(drag)seekAtClientX(e.clientX);});
|
||
addEventListener('mouseup',function(){drag=false;});
|
||
tl.addEventListener('touchstart',function(e){drag=true;seekAtClientX(e.touches[0].clientX);e.preventDefault();},{passive:false});
|
||
addEventListener('touchmove',function(e){if(drag){seekAtClientX(e.touches[0].clientX);e.preventDefault();}},{passive:false});
|
||
addEventListener('touchend',function(){drag=false;});
|
||
|
||
function curIndex(){
|
||
const t=A().currentTime, T=curTL();
|
||
if(t<T[0].t) return -1;
|
||
let ans=0;
|
||
for(let i=0;i<T.length;i++){ if(T[i].t<=t) ans=i; else break; }
|
||
return ans;
|
||
}
|
||
let lastRole=null;
|
||
function highlight(role){ if(role===lastRole)return; lastRole=role; for(const r in chars){ const on=(r===role); chars[r].active=on; chars[r].ring.visible=on; chars[r].bulb.visible=on; chars[r].halo.visible=on; if(!on)chars[r].blight.intensity=0; } }
|
||
const fmt=function(s){s=Math.max(0,s|0);return String(s/60|0).padStart(2,'0')+':'+String(s%60).padStart(2,'0');};
|
||
const sub=document.getElementById('subtitle'),sW=document.getElementById('subWho'),sO=document.getElementById('subOrig'),sT=document.getElementById('subTr');
|
||
const lineIdEl=document.getElementById('lineId');
|
||
function update(){
|
||
const a=A(), T=curTL(), dur=curDur();
|
||
document.getElementById('time').textContent=fmt(a.currentTime)+' / '+fmt(dur);
|
||
const i=curIndex();
|
||
if(i>=0){ const b=T[i]; highlight(b.rol);
|
||
let badge=(mode==='tr'&&!TR_READY())?' <span class="badge">ses hazırlanıyor</span>':'';
|
||
sW.innerHTML=(SHORT[b.rol]||b.rol)+badge; sW.style.color=(ROLES[b.rol]&&ROLES[b.rol].renk)||'#fff';
|
||
sO.textContent=b.orig; sT.textContent=b.tr; sub.style.opacity=1;
|
||
lineIdEl.textContent='#'+i;
|
||
} else { highlight(null); sub.style.opacity=.6; lineIdEl.textContent='#—'; }
|
||
drawTL();
|
||
}
|
||
ao.addEventListener('timeupdate',function(){if(mode==='orig'||!TR_READY())update();});
|
||
at.addEventListener('timeupdate',function(){if(mode==='tr'&&TR_READY())update();});
|
||
ao.addEventListener('loadedmetadata',function(){fitTL();update();});
|
||
at.addEventListener('loadedmetadata',function(){if(mode==='tr')fitTL();});
|
||
|
||
const playBtn=document.getElementById('play');
|
||
playBtn.onclick=function(){ const a=A(); if(a.paused){a.play();playBtn.textContent='⏸';}else{a.pause();playBtn.textContent='▶';} };
|
||
document.getElementById('prev').onclick=function(){ const T=curTL(); let i=curIndex(); const a=A(); if(i>0&&a.currentTime-T[i].t<0.4)i--; a.currentTime=T[Math.max(0,i)].t; update(); };
|
||
document.getElementById('next').onclick=function(){ const T=curTL(); let i=curIndex(); const a=A(); a.currentTime=T[Math.min(T.length-1,i+1)].t; update(); };
|
||
document.addEventListener('keydown',function(e){ if(e.code==='Space'){e.preventDefault();playBtn.onclick();} if(e.code==='ArrowRight')A().currentTime+=5; if(e.code==='ArrowLeft')A().currentTime-=5; });
|
||
|
||
document.getElementById('langToggle').addEventListener('click',function(e){
|
||
const btn=e.target.closest('button'); if(!btn)return; const m=btn.dataset.m; if(m===mode)return;
|
||
[].forEach.call(e.currentTarget.children,function(b){ b.classList.toggle('on',b===btn); b.classList.toggle('trm',b===btn&&m==='tr'); });
|
||
const wasPlaying=!A().paused; const idx=curIndex(); A().pause();
|
||
mode=m; sub.classList.toggle('trmode',m==='tr');
|
||
const T=curTL(); const b=T[Math.max(0,idx)]; if(b)A().currentTime=b.t;
|
||
fitTL(); update(); if(wasPlaying){A().play();playBtn.textContent='⏸';}
|
||
});
|
||
|
||
const roleNextIdx={};
|
||
function linesForRole(role){ return curTL().filter(function(x){return x.rol===role;}); }
|
||
renderer.domElement.addEventListener('click',function(ev){
|
||
const rc=new THREE.Raycaster(), m=new THREE.Vector2((ev.clientX/innerWidth)*2-1,-(ev.clientY/innerHeight)*2+1);
|
||
rc.setFromCamera(m,camera);
|
||
const gs=Object.keys(chars).map(function(r){return chars[r].grp;});
|
||
const hit=rc.intersectObjects(gs,true)[0]; if(!hit)return;
|
||
let g=hit.object; while(g.parent&&gs.indexOf(g)<0)g=g.parent;
|
||
const role=Object.keys(chars).find(function(r){return chars[r].grp===g;}); if(!role)return;
|
||
const lines=linesForRole(role); if(!lines.length)return;
|
||
let idx=roleNextIdx[role]||0;
|
||
if(idx>=lines.length)idx=0;
|
||
const b=lines[idx];
|
||
const a=A();
|
||
a.currentTime=Math.max(0,b.t-0.1);
|
||
roleNextIdx[role]=idx+1;
|
||
update();
|
||
a.play(); playBtn.textContent='⏸';
|
||
});
|
||
|
||
function loop(){
|
||
requestAnimationFrame(loop);
|
||
const t=performance.now()/1000;
|
||
for(const r in chars){ const c=chars[r];
|
||
if(c.active){ c.grp.position.y=Math.sin(t*4)*0.04; c.ring.rotation.z+=0.02; c.ring.material.emissiveIntensity=1.1+Math.sin(t*6)*0.5;
|
||
c.bulb.material.emissiveIntensity=1.6+Math.sin(t*6)*0.9; c.blight.intensity=1.4+Math.sin(t*6)*0.9;
|
||
c.halo.material.opacity=0.22+Math.sin(t*6)*0.12; c.halo.scale.setScalar(1+Math.sin(t*6)*0.12); }
|
||
else if(c.grp.position.y!==0){ c.grp.position.y*=0.85; if(Math.abs(c.grp.position.y)<0.002)c.grp.position.y=0; }
|
||
}
|
||
controls.update(); renderer.render(scene,camera);
|
||
}
|
||
loop();
|
||
addEventListener('resize',function(){ fitCamera(); renderer.setSize(innerWidth,innerHeight); fitTL(); });
|
||
fitTL();
|
||
</script>
|
||
</body>
|
||
</html>
|