349 lines
15 KiB
HTML
349 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="tr">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Nexus Orbits · İnteraktif Güneş Sistemi</title>
|
||
<style>
|
||
:root{
|
||
--bg:#04060f; --bg2:#0a0f24; --accent:#5eead4; --accent2:#818cf8;
|
||
--text:#e6ecff; --muted:#8b95b8; --panel:rgba(14,19,40,.72);
|
||
--border:rgba(120,140,220,.22);
|
||
font-family:"Segoe UI",Inter,system-ui,-apple-system,sans-serif;
|
||
}
|
||
*{margin:0;padding:0;box-sizing:border-box;}
|
||
html,body{height:100%;}
|
||
body{
|
||
background:radial-gradient(1200px 800px at 70% 10%, #131a3d 0%, var(--bg2) 40%, var(--bg) 100%);
|
||
color:var(--text);overflow:hidden;-webkit-font-smoothing:antialiased;
|
||
}
|
||
#space{position:fixed;inset:0;display:block;cursor:grab;touch-action:none;}
|
||
#space:active{cursor:grabbing;}
|
||
header{
|
||
position:fixed;top:0;left:0;right:0;z-index:10;
|
||
display:flex;align-items:center;justify-content:space-between;padding:16px 22px;
|
||
background:linear-gradient(180deg, rgba(4,6,15,.85), transparent);pointer-events:none;
|
||
}
|
||
header .brand{display:flex;align-items:center;gap:12px;pointer-events:auto;}
|
||
.logo{width:34px;height:34px;border-radius:50%;
|
||
background:radial-gradient(circle at 35% 30%, #a5f3fc, #6366f1 55%, #1e1b4b);
|
||
box-shadow:0 0 22px rgba(99,102,241,.7), 0 0 44px rgba(94,234,212,.25);position:relative;}
|
||
.logo::after{content:"";position:absolute;inset:-8px;border-radius:50%;
|
||
border:1px solid rgba(129,140,248,.5);border-top-color:transparent;border-left-color:transparent;
|
||
animation:spin 4s linear infinite;}
|
||
@keyframes spin{to{transform:rotate(360deg);}}
|
||
.brand h1{font-size:17px;font-weight:700;letter-spacing:.5px;}
|
||
.brand h1 span{color:var(--accent);}
|
||
.brand p{font-size:11px;color:var(--muted);letter-spacing:2px;text-transform:uppercase;}
|
||
.controls{display:flex;gap:8px;pointer-events:auto;}
|
||
.btn{background:var(--panel);border:1px solid var(--border);color:var(--text);
|
||
padding:9px 14px;border-radius:10px;font-size:13px;font-weight:500;cursor:pointer;
|
||
backdrop-filter:blur(10px);transition:.18s;display:flex;align-items:center;gap:7px;}
|
||
.btn:hover{border-color:var(--accent);color:var(--accent);transform:translateY(-1px);}
|
||
.btn:active{transform:translateY(0);}
|
||
.btn.off{opacity:.5;}
|
||
#info{position:fixed;left:22px;bottom:22px;z-index:10;width:min(340px, calc(100vw - 44px));
|
||
background:var(--panel);border:1px solid var(--border);border-radius:18px;padding:20px 22px;
|
||
backdrop-filter:blur(14px);box-shadow:0 20px 50px rgba(0,0,0,.5);transition:.3s;}
|
||
#info .tag{font-size:10px;letter-spacing:2px;text-transform:uppercase;color:var(--accent);
|
||
display:inline-flex;align-items:center;gap:6px;margin-bottom:8px;}
|
||
#info .tag::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--accent);
|
||
box-shadow:0 0 10px var(--accent);}
|
||
#info h2{font-size:22px;font-weight:700;margin-bottom:4px;}
|
||
#info .desc{font-size:13px;color:var(--muted);line-height:1.5;margin-bottom:14px;}
|
||
#stats{display:grid;grid-template-columns:1fr 1fr;gap:10px;}
|
||
#stats .st{background:rgba(255,255,255,.03);border:1px solid var(--border);border-radius:11px;padding:9px 11px;}
|
||
#stats .st b{display:block;font-size:15px;color:var(--accent2);}
|
||
#stats .st small{font-size:10px;color:var(--muted);letter-spacing:.5px;}
|
||
#hint{position:fixed;bottom:26px;right:24px;z-index:10;font-size:12px;color:var(--muted);
|
||
text-align:right;line-height:1.7;}
|
||
#hint kbd{background:var(--panel);border:1px solid var(--border);border-radius:6px;
|
||
padding:2px 6px;font-size:11px;color:var(--text);}
|
||
.speed{position:fixed;top:16px;left:50%;transform:translateX(-50%);z-index:10;
|
||
display:flex;align-items:center;gap:10px;pointer-events:auto;background:var(--panel);
|
||
border:1px solid var(--border);border-radius:12px;padding:8px 14px;backdrop-filter:blur(10px);
|
||
font-size:12px;color:var(--muted);}
|
||
.speed input{width:120px;accent-color:var(--accent);cursor:pointer;}
|
||
@media(max-width:720px){
|
||
header{padding:12px 14px;}
|
||
.brand p{display:none;}
|
||
#info{left:12px;right:12px;bottom:12px;width:auto;padding:16px;}
|
||
#hint{display:none;}
|
||
.speed{top:64px;left:12px;right:12px;transform:none;justify-content:space-between;}
|
||
.speed input{width:45%;}
|
||
.controls .btn span{display:none;}
|
||
.controls .btn{padding:9px 11px;}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<canvas id="space"></canvas>
|
||
|
||
<header>
|
||
<div class="brand">
|
||
<div class="logo"></div>
|
||
<div>
|
||
<h1>NEXUS <span>ORBITS</span></h1>
|
||
<p>İnteraktif Güneş Sistemi</p>
|
||
</div>
|
||
</div>
|
||
<div class="controls">
|
||
<button class="btn" id="btnPause">⏸ <span>Duraklat</span></button>
|
||
<button class="btn" id="btnTrail">✦ <span>İzler</span></button>
|
||
<button class="btn" id="btnReset">↺ <span>Sıfırla</span></button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="speed">
|
||
<span>Simülasyon Hızı</span>
|
||
<input type="range" id="speed" min="0" max="300" value="100" />
|
||
<b id="speedVal" style="color:var(--accent)">1.0×</b>
|
||
</div>
|
||
|
||
<div id="info">
|
||
<div class="tag" id="iTag">Güneş</div>
|
||
<h2 id="iName">Güneş</h2>
|
||
<div class="desc" id="iDesc">Sistemin merkezindeki yıldız. Bir gezegene tıklayarak keşfet.</div>
|
||
<div id="stats">
|
||
<div class="st"><b id="s1">696.340 km</b><small id="s1l">Yarıçap</small></div>
|
||
<div class="st"><b id="s2">—</b><small id="s2l">Yörünge</small></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="hint">
|
||
<kbd>Sol tık</kbd> gezegen seç · <kbd>Sürükle</kbd> kamera<br/>
|
||
<kbd>Tekerlek</kbd> yakınlaştır · <kbd>Boşluk</kbd> duraklat
|
||
</div>
|
||
|
||
<script>
|
||
(()=>{
|
||
const canvas=document.getElementById('space');
|
||
const ctx=canvas.getContext('2d');
|
||
let W,H,DPR;
|
||
function resize(){
|
||
DPR=Math.min(window.devicePixelRatio||1,2);
|
||
canvas.width=innerWidth*DPR; canvas.height=innerHeight*DPR;
|
||
canvas.style.width=innerWidth+'px'; canvas.style.height=innerHeight+'px';
|
||
ctx.setTransform(DPR,0,0,DPR,0,0);
|
||
W=innerWidth;H=innerHeight;
|
||
}
|
||
resize();
|
||
addEventListener('resize',resize);
|
||
|
||
const stars=[];
|
||
function buildStars(){
|
||
stars.length=0;
|
||
for(let i=0;i<340;i++){
|
||
stars.push({x:Math.random()*W,y:Math.random()*H,r:Math.random()*1.4+.2,
|
||
a:Math.random()*.7+.2,tw:Math.random()*Math.PI*2,ts:Math.random()*.03+.01});
|
||
}
|
||
}
|
||
buildStars();
|
||
addEventListener('resize',buildStars);
|
||
|
||
const planets=[
|
||
{name:"Merkür",tag:"İç Gezegen",color:"#c9a97a",radius:4,orbit:78,speed:1.6,
|
||
desc:"Güneş'e en yakın, en küçük gezegen. Yüzeyi kraterlerle kaplı.",
|
||
d1:["2.440 km","Yarıçap"],d2:["88 gün","Yörünge"]},
|
||
{name:"Venüs",tag:"İç Gezegen",color:"#e8b96b",radius:7,orbit:112,speed:1.18,
|
||
desc:"Kalın atmosferiyle sera etkisinin en güçlü olduğu gezegen.",
|
||
d1:["6.051 km","Yarıçap"],d2:["225 gün","Yörünge"]},
|
||
{name:"Dünya",tag:"Yaşam Gezegeni",color:"#4aa8ff",radius:7.5,orbit:150,speed:.92,
|
||
desc:"Bilinen tek yaşam barındıran gezegen. Mavi okyanuslar ve kıtalar.",
|
||
d1:["6.371 km","Yarıçap"],d2:["365 gün","Yörünge"]},
|
||
{name:"Mars",tag:"Kızıl Gezegen",color:"#e0592f",radius:6,orbit:188,speed:.74,
|
||
desc:"Demir oksit yüzeyiyle kızıl görünen gezegen, iki küçük uydusu var.",
|
||
d1:["3.389 km","Yarıçap"],d2:["687 gün","Yörünge"]},
|
||
{name:"Jüpiter",tag:"Gaz Devi",color:"#d9a066",radius:15,orbit:255,speed:.42,
|
||
desc:"Sistemin en büyük gezegeni. Büyük Kırmızı Leke bir dev fırtına.",
|
||
d1:["69.911 km","Yarıçap"],d2:["11,8 yıl","Yörünge"]},
|
||
{name:"Satürn",tag:"Halkalı Devi",color:"#e5cf8f",radius:12.5,orbit:315,speed:.31,ring:true,
|
||
desc:"Etkileyici buz halkalarıyla bilinen, yoğunluğu sudan düşük gaz devi.",
|
||
d1:["58.232 km","Yarıçap"],d2:["29,4 yıl","Yörünge"]},
|
||
{name:"Uranüs",tag:"Buz Devi",color:"#8fd8de",radius:9.5,orbit:365,speed:.22,
|
||
desc:"Yana yatmış ekseniyle dönen, soluk mavi buz devi.",
|
||
d1:["25.362 km","Yarıçap"],d2:["84 yıl","Yörünge"]},
|
||
{name:"Neptün",tag:"Buz Devi",color:"#5b7ff0",radius:9,orbit:410,speed:.16,
|
||
desc:"Güneş'ten en uzak gezegen. Süpersonik rüzgarlarıyla ünlü.",
|
||
d1:["24.622 km","Yarıçap"],d2:["165 yıl","Yörünge"]}
|
||
];
|
||
planets.forEach(p=>{ p.angle=Math.random()*Math.PI*2; p.trail=[]; });
|
||
|
||
const moon={angle:0,orbit:16,radius:2.6,color:"#cfd6e6"};
|
||
|
||
let camX=0,camY=0,zoom=.6,targetZoom=1;
|
||
let paused=false,showTrails=true,speedMul=1,selected=null;
|
||
let last=performance.now();
|
||
|
||
function worldToScreen(p){
|
||
const r=p.orbit*zoom;
|
||
const cx=W/2+camX, cy=H/2+camY;
|
||
return {x:cx+Math.cos(p.angle)*r,y:cy+Math.sin(p.angle)*r};
|
||
}
|
||
|
||
let dragging=false,dragMoved=false,lastX=0,lastY=0;
|
||
canvas.addEventListener('pointerdown',e=>{
|
||
dragging=true;dragMoved=false;lastX=e.clientX;lastY=e.clientY;
|
||
canvas.setPointerCapture(e.pointerId);
|
||
});
|
||
canvas.addEventListener('pointermove',e=>{
|
||
if(dragging){
|
||
const dx=e.clientX-lastX, dy=e.clientY-lastY;
|
||
if(Math.abs(dx)+Math.abs(dy)>3) dragMoved=true;
|
||
camX+=dx;camY+=dy;lastX=e.clientX;lastY=e.clientY;return;
|
||
}
|
||
let hit=false;
|
||
for(const p of planets){
|
||
const s=worldToScreen(p);
|
||
if(Math.hypot(s.x-e.clientX,s.y-e.clientY)<Math.max(p.radius*zoom,16)){hit=true;break;}
|
||
}
|
||
canvas.style.cursor=hit?'pointer':'grab';
|
||
});
|
||
canvas.addEventListener('pointerup',e=>{
|
||
dragging=false;
|
||
if(!dragMoved) pick(e.clientX,e.clientY);
|
||
});
|
||
canvas.addEventListener('wheel',e=>{
|
||
e.preventDefault();
|
||
targetZoom*=(e.deltaY<0?1.12:0.89);
|
||
targetZoom=Math.max(.45,Math.min(2.6,targetZoom));
|
||
},{passive:false});
|
||
addEventListener('keydown',e=>{ if(e.code==='Space'){e.preventDefault();togglePause();} });
|
||
|
||
function pick(mx,my){
|
||
let best=null,bd=1e9;
|
||
for(const p of planets){
|
||
const s=worldToScreen(p);
|
||
const d=Math.hypot(s.x-mx,s.y-my);
|
||
if(d<Math.max(p.radius*zoom,16)&&d<bd){bd=d;best=p;}
|
||
}
|
||
selected=best; updateInfo(best);
|
||
}
|
||
|
||
const iTag=document.getElementById('iTag'),iName=document.getElementById('iName'),
|
||
iDesc=document.getElementById('iDesc'),s1=document.getElementById('s1'),
|
||
s2=document.getElementById('s2'),s1l=document.getElementById('s1l'),s2l=document.getElementById('s2l');
|
||
function updateInfo(p){
|
||
if(!p){
|
||
iTag.textContent="Güneş";iName.textContent="Güneş";
|
||
iDesc.textContent="Sistemin merkezindeki yıldız. Bir gezegene tıklayarak keşfet.";
|
||
s1.textContent="696.340 km";s1l.textContent="Yarıçap";s2.textContent="—";s2l.textContent="Yörünge";return;
|
||
}
|
||
iTag.textContent=p.tag;iName.textContent=p.name;iDesc.textContent=p.desc;
|
||
s1.textContent=p.d1[0];s1l.textContent=p.d1[1];s2.textContent=p.d2[0];s2l.textContent=p.d2[1];
|
||
}
|
||
updateInfo(null);
|
||
|
||
const btnPause=document.getElementById('btnPause'),btnTrail=document.getElementById('btnTrail'),
|
||
btnReset=document.getElementById('btnReset'),speedEl=document.getElementById('speed'),
|
||
speedVal=document.getElementById('speedVal');
|
||
|
||
function togglePause(){
|
||
paused=!paused;
|
||
btnPause.innerHTML=paused?'▶ <span>Devam</span>':'⏸ <span>Duraklat</span>';
|
||
btnPause.classList.toggle('off',paused);
|
||
}
|
||
btnPause.onclick=togglePause;
|
||
btnTrail.onclick=()=>{showTrails=!showTrails;btnTrail.classList.toggle('off',!showTrails);planets.forEach(p=>p.trail.length=0);};
|
||
btnReset.onclick=()=>{
|
||
camX=0;camY=0;targetZoom=1;zoom=1;
|
||
planets.forEach(p=>{p.angle=Math.random()*Math.PI*2;p.trail.length=0;});
|
||
selected=null;updateInfo(null);
|
||
};
|
||
speedEl.oninput=()=>{speedMul=speedEl.value/100;speedVal.textContent=speedMul.toFixed(1)+'×';};
|
||
|
||
function lighten(hex,amt){
|
||
const n=parseInt(hex.replace('#',''),16);
|
||
let r=(n>>16)&255,g=(n>>8)&255,b=n&255;
|
||
r=Math.min(255,r+255*amt);g=Math.min(255,g+255*amt);b=Math.min(255,b+255*amt);
|
||
return `rgb(${r|0},${g|0},${b|0})`;
|
||
}
|
||
|
||
function drawStars(dt){
|
||
ctx.clearRect(0,0,W,H);
|
||
for(const s of stars){
|
||
s.tw+=s.ts*dt*60;
|
||
ctx.globalAlpha=s.a*(0.6+0.4*Math.sin(s.tw));
|
||
ctx.fillStyle='#cfe0ff';
|
||
ctx.beginPath();ctx.arc(s.x,s.y,s.r,0,7);ctx.fill();
|
||
}
|
||
ctx.globalAlpha=1;
|
||
}
|
||
function drawSun(dt){
|
||
const cx=W/2+camX, cy=H/2+camY, r=26*zoom*(1+0.02*Math.sin(dt*0));
|
||
const g=ctx.createRadialGradient(cx,cy,r*.2,cx,cy,r*4);
|
||
g.addColorStop(0,'rgba(255,214,120,.95)');
|
||
g.addColorStop(.25,'rgba(255,170,60,.45)');
|
||
g.addColorStop(.6,'rgba(255,120,40,.12)');
|
||
g.addColorStop(1,'rgba(255,120,40,0)');
|
||
ctx.fillStyle=g;ctx.beginPath();ctx.arc(cx,cy,r*4,0,7);ctx.fill();
|
||
const c=ctx.createRadialGradient(cx-r*.3,cy-r*.3,r*.1,cx,cy,r);
|
||
c.addColorStop(0,'#fffbe8');c.addColorStop(.5,'#ffcf57');c.addColorStop(1,'#ff8a1e');
|
||
ctx.fillStyle=c;ctx.beginPath();ctx.arc(cx,cy,r,0,7);ctx.fill();
|
||
}
|
||
function drawOrbits(){
|
||
const cx=W/2+camX, cy=H/2+camY;
|
||
ctx.lineWidth=1;
|
||
for(const p of planets){
|
||
ctx.strokeStyle= selected===p ? 'rgba(94,234,212,.5)' : 'rgba(140,160,230,.13)';
|
||
ctx.beginPath();ctx.arc(cx,cy,p.orbit*zoom,0,7);ctx.stroke();
|
||
}
|
||
}
|
||
function drawPlanet(p){
|
||
const s=worldToScreen(p), r=p.radius*zoom;
|
||
if(showTrails){
|
||
p.trail.push({x:s.x,y:s.y});
|
||
if(p.trail.length>26) p.trail.shift();
|
||
for(let i=0;i<p.trail.length;i++){
|
||
ctx.globalAlpha=(i/p.trail.length)*.35;
|
||
ctx.fillStyle=p.color;
|
||
ctx.beginPath();ctx.arc(p.trail[i].x,p.trail[i].y,r*.28,0,7);ctx.fill();
|
||
}
|
||
ctx.globalAlpha=1;
|
||
}
|
||
if(selected===p){
|
||
ctx.strokeStyle='rgba(94,234,212,.85)';ctx.lineWidth=1.5;ctx.setLineDash([4,4]);
|
||
ctx.beginPath();ctx.arc(s.x,s.y,r+9,0,7);ctx.stroke();ctx.setLineDash([]);
|
||
}
|
||
if(p.ring){
|
||
ctx.save();ctx.translate(s.x,s.y);ctx.rotate(-.4);
|
||
ctx.strokeStyle='rgba(229,207,143,.7)';ctx.lineWidth=Math.max(1.5,r*.28);
|
||
ctx.beginPath();ctx.ellipse(0,0,r*1.9,r*.62,0,0,7);ctx.stroke();
|
||
ctx.strokeStyle='rgba(229,207,143,.35)';ctx.lineWidth=Math.max(1,r*.14);
|
||
ctx.beginPath();ctx.ellipse(0,0,r*2.35,r*.78,0,0,7);ctx.stroke();
|
||
ctx.restore();
|
||
}
|
||
const gr=ctx.createRadialGradient(s.x-r*.35,s.y-r*.35,r*.1,s.x,s.y,r);
|
||
gr.addColorStop(0,lighten(p.color,.4));gr.addColorStop(1,p.color);
|
||
ctx.fillStyle=gr;ctx.shadowColor=p.color;ctx.shadowBlur=selected===p?26:12;
|
||
ctx.beginPath();ctx.arc(s.x,s.y,r,0,7);ctx.fill();
|
||
if(selected===p){
|
||
ctx.shadowBlur=0;ctx.fillStyle='#e6ecff';ctx.font='600 12px Segoe UI, sans-serif';
|
||
ctx.textAlign='center';ctx.fillText(p.name,s.x,s.y-r-15);
|
||
}
|
||
ctx.shadowBlur=0;
|
||
}
|
||
function drawMoon(dt){
|
||
const earth=planets.find(p=>p.name==="Dünya");
|
||
if(!earth)return;
|
||
const es=worldToScreen(earth);
|
||
moon.angle+=0.06*speedMul*(paused?0:1)*dt*6;
|
||
const mr=moon.orbit*zoom;
|
||
const mx=es.x+Math.cos(moon.angle)*mr, my=es.y+Math.sin(moon.angle)*mr;
|
||
ctx.fillStyle=moon.color;
|
||
ctx.beginPath();ctx.arc(mx,my,moon.radius*zoom,0,7);ctx.fill();
|
||
}
|
||
|
||
function loop(now){
|
||
const dt=Math.min((now-last)/1000,.05); last=now;
|
||
zoom+=(targetZoom-zoom)*.12;
|
||
if(!paused){ planets.forEach(p=>{ p.angle += p.speed*0.32*speedMul*dt; }); }
|
||
drawStars(dt); drawOrbits(); drawSun(dt); drawMoon(dt); planets.forEach(drawPlanet);
|
||
requestAnimationFrame(loop);
|
||
}
|
||
requestAnimationFrame(loop);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|