NEWTRY / index.html
fanboyd13's picture
Upload 7 files
7edb31d verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>πŸ‘— Virtual Try-On</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',sans-serif;background:#0f0f0f;color:#fff;min-height:100vh}
header{background:linear-gradient(135deg,#e879f9,#818cf8);padding:28px;text-align:center}
header h1{font-size:2.2rem;font-weight:800;letter-spacing:-1px}
header p{opacity:.85;margin-top:8px;font-size:1rem}
.container{max-width:1080px;margin:32px auto;padding:0 20px}
.model-bar{text-align:center;padding:12px 20px;border-radius:10px;margin-bottom:24px;font-size:.9rem;font-weight:600}
.loading{background:#1c1917;color:#fbbf24;border:1px solid #78350f}
.ready{background:#052e16;color:#4ade80;border:1px solid #166534}
.errored{background:#1c0606;color:#f87171;border:1px solid #7f1d1d}
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
@media(max-width:720px){.grid{grid-template-columns:1fr}}
.card{background:#161616;border-radius:16px;padding:20px;border:1px solid #272727}
.card h3{color:#c084fc;font-size:.95rem;margin-bottom:14px;text-transform:uppercase;letter-spacing:.05em}
.drop{border:2px dashed #333;border-radius:12px;min-height:240px;display:flex;
flex-direction:column;align-items:center;justify-content:center;gap:8px;
cursor:pointer;transition:.2s;padding:16px;position:relative}
.drop:hover{border-color:#c084fc;background:#1a1a1a}
.drop img{max-height:200px;max-width:100%;border-radius:8px;object-fit:contain}
.drop input{display:none}
.drop .em{font-size:2.8rem}
.drop .lbl{color:#555;font-size:.82rem;text-align:center}
.btn{width:100%;padding:14px;border:none;border-radius:12px;cursor:pointer;
font-size:1rem;font-weight:700;margin-top:14px;letter-spacing:.02em;
background:linear-gradient(135deg,#e879f9,#818cf8);color:#fff;transition:.2s}
.btn:hover{opacity:.85;transform:translateY(-1px)}
.btn:disabled{opacity:.35;cursor:not-allowed;transform:none}
.msg{text-align:center;margin-top:10px;font-size:.85rem;color:#888;min-height:18px}
.bar-wrap{height:4px;background:#222;border-radius:2px;margin-top:8px;overflow:hidden}
.bar{height:100%;width:0%;background:linear-gradient(90deg,#e879f9,#818cf8);
border-radius:2px;transition:width .8s ease}
.spin{display:none;width:36px;height:36px;border:3px solid #222;
border-top-color:#e879f9;border-radius:50%;animation:rot .7s linear infinite;margin:70px auto}
@keyframes rot{to{transform:rotate(360deg)}}
.res-img{width:100%;border-radius:10px;display:none}
.tip{background:#161616;border-radius:12px;padding:18px 20px;margin-top:24px;
border-left:4px solid #818cf8}
.tip p{color:#666;font-size:.85rem;line-height:1.7}
.tip b{color:#a5b4fc}
</style>
</head>
<body>
<header>
<h1>πŸ‘— Virtual Try-On</h1>
<p>Upload your photo + any garment β€” powered by AI running right here</p>
</header>
<div class="container">
<div class="model-bar loading" id="modelBar">⏳ AI model loading β€” ready in ~2 minutes on first start...</div>
<div class="grid">
<!-- Person -->
<div class="card">
<h3>πŸ‘€ Your Photo</h3>
<div class="drop" onclick="document.getElementById('pIn').click()">
<div class="em" id="pEm">🧍</div>
<div class="lbl" id="pLbl">Click to upload<br>Full body Β· Front facing</div>
<img id="pImg"/>
<input type="file" id="pIn" accept="image/*" onchange="preview(this,'pImg','pEm','pLbl','person')"/>
</div>
</div>
<!-- Garment -->
<div class="card">
<h3>πŸ‘• Garment</h3>
<div class="drop" onclick="document.getElementById('gIn').click()">
<div class="em" id="gEm">πŸ‘”</div>
<div class="lbl" id="gLbl">Click to upload<br>White bg recommended</div>
<img id="gImg"/>
<input type="file" id="gIn" accept="image/*" onchange="preview(this,'gImg','gEm','gLbl','garment')"/>
</div>
<button class="btn" id="btn" onclick="run()" disabled>✨ Try It On!</button>
<div class="msg" id="msg"></div>
<div class="bar-wrap"><div class="bar" id="bar"></div></div>
</div>
<!-- Result -->
<div class="card">
<h3>πŸŽ‰ Result</h3>
<div class="drop" style="cursor:default">
<div class="em" id="rEm">✨</div>
<div class="lbl" id="rLbl">Your try-on result<br>will appear here</div>
<div class="spin" id="spin"></div>
<img class="res-img" id="rImg"/>
</div>
</div>
</div>
<div class="tip">
<p><b>Tips for best results:</b> Use a clear front-facing full-body photo with good lighting. Garment images on plain white background produce the most realistic try-on. The AI will place the garment on your upper body area automatically.</p>
</div>
</div>
<script>
let files={person:null,garment:null}, modelReady=false;
async function pollModel(){
try{
const d=await(await fetch('/status')).json();
const bar=document.getElementById('modelBar');
if(d.loaded){
bar.className='model-bar ready'; bar.textContent='βœ… AI Model Ready!';
modelReady=true; updateBtn();
} else if(d.error){
bar.className='model-bar errored'; bar.textContent='❌ '+d.error;
} else { setTimeout(pollModel,4000); }
}catch(e){setTimeout(pollModel,4000);}
}
pollModel();
function preview(input,imgId,emId,lblId,key){
const f=input.files[0]; if(!f)return;
files[key]=f;
const r=new FileReader();
r.onload=e=>{
document.getElementById(emId).style.display='none';
document.getElementById(lblId).style.display='none';
const i=document.getElementById(imgId);
i.src=e.target.result; i.style.display='block';
};
r.readAsDataURL(f); updateBtn();
}
function updateBtn(){
document.getElementById('btn').disabled=!(files.person&&files.garment&&modelReady);
}
async function run(){
const btn=document.getElementById('btn');
const msg=document.getElementById('msg');
const spin=document.getElementById('spin');
const rImg=document.getElementById('rImg');
const rEm=document.getElementById('rEm');
const rLbl=document.getElementById('rLbl');
const bar=document.getElementById('bar');
btn.disabled=true; msg.textContent='⏳ Processing (~30-60s)...';
spin.style.display='block'; rImg.style.display='none';
rEm.style.display='none'; rLbl.style.display='none';
bar.style.width='0%';
let p=0; const iv=setInterval(()=>{p=Math.min(p+1.5,88);bar.style.width=p+'%';},1000);
const fd=new FormData();
fd.append('person',files.person);
fd.append('garment',files.garment);
try{
const res=await fetch('/tryon',{method:'POST',body:fd});
const d=await res.json();
clearInterval(iv); bar.style.width='100%';
spin.style.display='none';
if(d.status==='ok'){
rImg.src='data:image/png;base64,'+d.image;
rImg.style.display='block';
msg.textContent='βœ… Done! Download or try another outfit.';
} else if(d.status==='loading'){
msg.textContent='⏳ Still warming up, retrying in 20s...';
rEm.style.display='block'; rLbl.style.display='block'; rEm.textContent='⏳';
setTimeout(run,20000); return;
} else {
msg.textContent='❌ '+d.message;
rEm.style.display='block'; rLbl.style.display='block';
}
}catch(e){
clearInterval(iv); spin.style.display='none';
msg.textContent='❌ '+e.message;
rEm.style.display='block'; rLbl.style.display='block';
}
btn.disabled=false;
}
</script>
</body>
</html>