Skip to content

Commit 34a1945

Browse files
authored
hehe
1 parent 1ca613d commit 34a1945

1 file changed

Lines changed: 205 additions & 100 deletions

File tree

index.html

Lines changed: 205 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -4,127 +4,232 @@
44
<meta charset="utf-8" />
55
<meta name="viewport" content="width=device-width,initial-scale=1" />
66
<title>Bismay • Portfolio</title>
7-
<meta name="description" content="Portfolio — Bismay • minimal black & white UI with interactive 3D-style project cards" />
7+
<meta name="description" content="Bismay-exe portfolio — PNG-sequence hero + auto-fetched GitHub projects" />
88
<style>
9-
:root{--bg:#fff;--fg:#000;--muted:#666;--accent:#000}
9+
:root{--bg:#fff;--fg:#000;--muted:#666}
1010
*{box-sizing:border-box}
11-
html,body{height:100%;margin:0;background:var(--bg);color:var(--fg);font-family:Inter,system-ui,Arial,Helvetica,sans-serif}
11+
html,body{height:100%;margin:0;background:var(--bg);color:var(--fg);font-family:Inter,system-ui,Arial,Helvetica,sans-serif;-webkit-font-smoothing:antialiased}
1212
a{color:inherit;text-decoration:none}
13+
14+
/* Hero (PNG sequence) */
15+
#hero{height:100vh; display:grid; place-items:center; position:relative; overflow:hidden}
16+
canvas#sequence{max-width:90vw; max-height:85vh; display:block}
17+
#loading{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-family:monospace;color:var(--muted)}
18+
19+
/* content */
1320
.container{max-width:1100px;margin:0 auto;padding:48px 24px}
21+
.hero-copy{position:absolute;top:36px;left:36px;z-index:20}
22+
.title{font-weight:800;font-size:28px}
23+
.subtitle{color:var(--muted);margin-top:6px}
24+
25+
/* Projects */
26+
section#projects{padding:56px 0}
27+
h2{font-size:20px;margin:0 0 10px}
28+
p.lead{color:var(--muted);margin-top:6px}
1429

15-
/* Hero */
16-
.hero{display:flex;align-items:center;gap:28px;flex-wrap:wrap}
17-
.title{font-weight:800;font-size:clamp(28px,6vw,54px);line-height:1}
18-
.subtitle{color:var(--muted);margin-top:8px;font-size:16px}
19-
20-
/* Animated words */
21-
.animated-words{display:inline-block;margin-left:6px;vertical-align:middle}
22-
.animated-words span{display:inline-block;opacity:0;transform:translateY(6px);animation:wordIn .9s forwards;}
23-
.animated-words span:nth-child(1){animation-delay:.1s}
24-
.animated-words span:nth-child(2){animation-delay:.35s}
25-
.animated-words span:nth-child(3){animation-delay:.6s}
26-
@keyframes wordIn{to{opacity:1;transform:none}}
27-
28-
/* Grid */
29-
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:40px}
30+
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:24px}
3031
@media(max-width:980px){.grid{grid-template-columns:repeat(2,1fr)}}
3132
@media(max-width:640px){.grid{grid-template-columns:1fr}}
3233

33-
/* Card */
34-
.card{background:#fff;border:1px solid rgba(0,0,0,0.06);border-radius:14px;padding:18px;box-shadow:0 10px 30px rgba(0,0,0,0.06);cursor:pointer;transform-style:preserve-3d;transition:transform .25s ease, box-shadow .25s ease}
35-
.card:hover{box-shadow:0 22px 60px rgba(0,0,0,0.12);}
34+
.card{background:var(--bg);border:1px solid rgba(0,0,0,0.06);border-radius:12px;padding:14px;box-shadow:0 8px 30px rgba(0,0,0,0.04);cursor:pointer;transform-style:preserve-3d;transition:transform .22s ease, box-shadow .22s ease}
35+
.card:hover{box-shadow:0 26px 60px rgba(0,0,0,0.08)}
3636
.card-inner{transform-style:preserve-3d;transition:transform .18s ease}
37-
.card-thumb{height:140px;border-radius:10px;overflow:hidden;background:linear-gradient(180deg,#f6f6f6,#e9e9e9);display:flex;align-items:center;justify-content:center;color:var(--muted);font-weight:700}
37+
.card-thumb{height:120px;border-radius:8px;overflow:hidden;background:linear-gradient(180deg,#fafafa,#f0f0f0);display:flex;align-items:center;justify-content:center;color:var(--muted);font-weight:700}
3838
.card-title{font-weight:700;margin-top:12px}
39-
.card-desc{color:var(--muted);font-size:14px;margin-top:8px}
40-
.card-meta{display:flex;gap:10px;align-items:center;margin-top:14px;font-size:13px;color:var(--muted)}
39+
.card-desc{color:var(--muted);font-size:14px;margin-top:8px;min-height:44px}
40+
.card-meta{display:flex;gap:8px;align-items:center;margin-top:10px;font-size:13px;color:var(--muted);flex-wrap:wrap}
41+
.badge{display:inline-block;padding:4px 8px;border-radius:999px;background:#fff;color:#000;font-size:12px;margin:2px;box-shadow:0 1px 3px rgba(0,0,0,0.12)}
42+
.meta-right{margin-left:auto;font-weight:600}
4143

42-
/* 3D hover perspective */
43-
.card-3d{perspective:900px}
44+
footer{padding:36px 0;color:var(--muted);text-align:center}
4445

45-
/* Footer */
46-
footer{margin-top:56px;padding:24px 0;color:var(--muted);text-align:center}
46+
/* help overlay (draggable desktop only) */
47+
#help-overlay{position:fixed;top:20px;right:20px;background:rgba(0,0,0,0.8);color:#fff;padding:12px 14px;border-radius:10px;font-size:13px;z-index:999;cursor:grab}
48+
#help-overlay:active{cursor:grabbing}
4749
</style>
4850
</head>
4951
<body>
50-
<div class="container">
51-
<header class="hero">
52-
<div>
53-
<div class="title">Bismay • <span style="opacity:.8">Developer</span></div>
54-
<div class="subtitle">Minimal, modern projects — <span class="animated-words"><span>Design</span><span>Code</span><span>Ship</span></span></div>
55-
</div>
56-
<div style="margin-left:auto;text-align:right">
57-
<div style="font-weight:700">GitHub</div>
58-
<div style="color:var(--muted)"><a href="https://github.com/Bismay-exe" target="_blank" rel="noopener">github.com/Bismay-exe</a></div>
59-
</div>
60-
</header>
61-
62-
<section aria-labelledby="projects">
63-
<h2 id="projects" style="margin-top:48px">Selected projects</h2>
64-
<p style="color:var(--muted)">Projects pulled from the public GitHub profile. Click any card to view the repo.</p>
65-
66-
<div class="grid" id="projects-grid">
67-
<!-- Cards will be injected here -->
68-
</div>
52+
<header id="hero">
53+
<canvas id="sequence"></canvas>
54+
<div id="loading">Loading...</div>
55+
<div class="hero-copy">
56+
<div class="title">Bismay • Developer</div>
57+
<div class="subtitle">Interactive portfolio — scroll to explore</div>
58+
</div>
59+
</header>
60+
61+
<main class="container">
62+
<section id="projects">
63+
<h2>Projects</h2>
64+
<p class="lead">All public repos from <strong>Bismay-exe</strong> (pulled live from GitHub)</p>
65+
<div class="grid" id="projects-grid"></div>
6966
</section>
67+
<footer>© <span id="year"></span> Bismay • Built with HTML/CSS/JS</footer>
68+
</main>
7069

71-
<footer>
72-
© <span id="year"></span> Bismay • Built with plain HTML/CSS/JS
73-
</footer>
74-
</div>
70+
<div id="help-overlay">Controls: Scroll to spin • Drag (desktop) • Keys: ← → space R L H</div>
7571

7672
<script>
77-
// Project list sourced from GitHub profile (public repos); see README for live links.
78-
const projects = [
79-
{name:'Beautiful-Notes', desc:'Sleek notes app with glass UI', url:'https://github.com/Bismay-exe/Beautiful-Notes'},
80-
{name:'5GTile', desc:'5G toggle / Tile project (Kotlin)', url:'https://github.com/Bismay-exe/5GTile'},
81-
{name:'Mechanical-Calculator', desc:'All-in-one calculator with themes', url:'https://github.com/Bismay-exe/Mechanical-Calculator'},
82-
{name:'My-Heavenly-Archive', desc:'Media gallery with filters & lightbox', url:'https://github.com/Bismay-exe/My-Heavenly-Archive'},
83-
{name:'Crypto-converter', desc:'Realtime crypto ↔ INR/USD/EUR converter', url:'https://github.com/Bismay-exe/Crypto-converter'},
84-
{name:'login-page', desc:'Simple minimal login page template', url:'https://github.com/Bismay-exe/login-page'},
85-
{name:'Bismay-exe', desc:'Personal profile & readme repo', url:'https://github.com/Bismay-exe/Bismay-exe'},
86-
{name:'Beautiful-Notes-Showcase', desc:'(extra) project showcase', url:'https://github.com/Bismay-exe/Beautiful-Notes-Showcase'}
87-
];
88-
89-
// Build cards
90-
const grid = document.getElementById('projects-grid');
91-
projects.forEach((p, idx) => {
92-
const col = document.createElement('a');
93-
col.className = 'card card-3d';
94-
col.href = p.url; col.target = '_blank'; col.rel = 'noopener';
95-
96-
const inner = document.createElement('div'); inner.className = 'card-inner';
97-
const thumb = document.createElement('div'); thumb.className = 'card-thumb'; thumb.textContent = p.name;
98-
const title = document.createElement('div'); title.className = 'card-title'; title.textContent = p.name.replace(/[-_]/g,' ');
99-
const desc = document.createElement('div'); desc.className = 'card-desc'; desc.textContent = p.desc;
100-
const meta = document.createElement('div'); meta.className = 'card-meta'; meta.innerHTML = `<span>GitHub</span><span>•</span><span>View Repo</span>`;
101-
102-
inner.appendChild(thumb); inner.appendChild(title); inner.appendChild(desc); inner.appendChild(meta);
103-
col.appendChild(inner);
104-
grid.appendChild(col);
105-
106-
// 3D hover effect: mousemove on card
107-
col.addEventListener('mousemove', (e)=>{
108-
const rect = col.getBoundingClientRect();
109-
const x = (e.clientX - rect.left) / rect.width - 0.5; // -0.5 -> 0.5
110-
const y = (e.clientY - rect.top) / rect.height - 0.5;
111-
const tiltX = (y * -1) * 12; // rotateX
112-
const tiltY = x * 12; // rotateY
113-
inner.style.transform = `rotateX(${tiltX}deg) rotateY(${tiltY}deg) translateZ(8px)`;
114-
});
115-
col.addEventListener('mouseleave', ()=>{
116-
inner.style.transform = 'rotateX(0deg) rotateY(0deg) translateZ(0px)';
117-
});
118-
119-
// subtle entrance animation stagger
120-
inner.style.opacity = 0; inner.style.transform += ' translateY(10px)';
121-
setTimeout(()=>{ inner.style.transition='transform .5s ease, opacity .5s ease'; inner.style.opacity=1; inner.style.transform='none'; }, idx*80);
73+
// ========== PNG SEQUENCE HERO ===========
74+
const canvas = document.getElementById('sequence');
75+
const ctx = canvas.getContext('2d');
76+
let W = window.innerWidth, H = window.innerHeight;
77+
function resize(){ W = window.innerWidth; H = window.innerHeight; canvas.width = W; canvas.height = H; }
78+
window.addEventListener('resize', resize); resize();
79+
80+
const TOTAL = 300; // male0001.png ... male0300.png
81+
const cache = {};
82+
const buffer = 8;
83+
let current = 1, velocity = 0, firstLoaded=false;
84+
let scale = 1;
85+
let tiltX=0, tiltY=0;
86+
87+
function frameFile(n){ return `male${String(n).padStart(4,'0')}.png`; }
88+
89+
function loadFrame(n, cb){
90+
if (n < 1) n += TOTAL; if (n > TOTAL) n -= TOTAL;
91+
if (cache[n]) { if(cb) cb(cache[n]); return; }
92+
const i = new Image(); i.src = frameFile(n);
93+
i.onload = ()=>{ cache[n] = i; if(!firstLoaded){ firstLoaded=true; document.getElementById('loading').style.display='none'; } if(cb) cb(i); };
94+
i.onerror = ()=>{ /* ignore */ };
95+
}
96+
97+
// load first frame
98+
loadFrame(1, (img)=>{ draw(1); preload(1); });
99+
function preload(center){ for(let off=-buffer; off<=buffer; off++){ let idx = Math.round(center) + off; if(idx<1) idx+=TOTAL; if(idx>TOTAL) idx-=TOTAL; loadFrame(idx); } }
100+
101+
function draw(frame){ const img = cache[Math.round(frame)]; if(!img) return; ctx.clearRect(0,0,W,H); const iw = img.width*scale; const ih = img.height*scale; const x = (W - iw)/2 + tiltX*30; const y = (H - ih)/2 + tiltY*30; ctx.drawImage(img, x, y, iw, ih); }
102+
103+
function update(){
104+
current += velocity; velocity *= 0.92;
105+
if(current > TOTAL) current = 1; if(current < 1) current = TOTAL;
106+
const idx = Math.round(current);
107+
if(cache[idx]) draw(current);
108+
preload(idx);
109+
requestAnimationFrame(update);
110+
}
111+
requestAnimationFrame(update);
112+
113+
// wheel -> velocity
114+
window.addEventListener('wheel', (e)=>{ velocity += Math.sign(e.deltaY) * 2.2; }, {passive:true});
115+
// mouse drag
116+
let lastY=0; window.addEventListener('mousedown', (e)=>{ lastY = e.clientY; });
117+
window.addEventListener('mousemove', (e)=>{ if(e.buttons){ const d = e.clientY - lastY; velocity += d*0.06; lastY = e.clientY; } tiltX = (e.clientX / W - 0.5); tiltY = (e.clientY / H - 0.5); });
118+
// touch swipe
119+
let tx=0, ty=0, tstart=0, lastTap=0, tapTimeout=null; let pinchDist=null;
120+
window.addEventListener('touchstart',(e)=>{ if(e.touches.length===1){ tx=e.touches[0].clientX; ty=e.touches[0].clientY; tstart=Date.now(); } if(e.touches.length===2){ pinchDist = Math.hypot(e.touches[0].clientX-e.touches[1].clientX, e.touches[0].clientY-e.touches[1].clientY); } });
121+
window.addEventListener('touchmove',(e)=>{ if(e.touches.length===1){ const dx = e.touches[0].clientX - tx; const dy = e.touches[0].clientY - ty; velocity += -dx*0.04; tx = e.touches[0].clientX; ty = e.touches[0].clientY; tiltX = (tx / W - 0.5); tiltY = (ty / H - 0.5); } else if(e.touches.length===2){ const dist = Math.hypot(e.touches[0].clientX-e.touches[1].clientX, e.touches[0].clientY-e.touches[1].clientY); if(pinchDist){ const delta = dist - pinchDist; scale = Math.min(2, Math.max(0.5, scale + delta*0.003)); } pinchDist = dist; } }, {passive:false});
122+
window.addEventListener('touchend',(e)=>{ if(e.touches.length===0 && e.changedTouches.length===1){ const dt = Date.now()-tstart; if(dt<200) { /* quick flick */ velocity *= 2.2; } // tap/double-tap handling
123+
const now = Date.now(); const gap = now - lastTap; if(gap<300 && gap>0){ // double tap -> stop
124+
velocity = 0; clearTimeout(tapTimeout);
125+
} else {
126+
tapTimeout = setTimeout(()=>{ /* single tap -> reset */ current = 1; },250);
127+
} lastTap = now;
128+
} if(e.touches.length<2) pinchDist = null; });
129+
130+
// keyboard controls
131+
window.addEventListener('keydown',(e)=>{
132+
if(e.key==='ArrowRight') velocity += 3;
133+
if(e.key==='ArrowLeft') velocity -= 3;
134+
if(e.key===' ') velocity = 0;
135+
if(e.key.toLowerCase()==='r') current = 1;
136+
if(e.key.toLowerCase()==='l') current = TOTAL;
137+
if(e.key==='+') scale = Math.min(2, scale+0.1);
138+
if(e.key==='-') scale = Math.max(0.5, scale-0.1);
139+
if(e.key.toLowerCase()==='h'){ const o=document.getElementById('help-overlay'); o.style.opacity=(o.style.opacity==='0'? '1':'0'); }
122140
});
123141

124-
// small accessibility: keyboard focus styling
125-
document.addEventListener('keydown', (e)=>{
126-
if(e.key === 'Tab') document.body.classList.add('show-focus');
127-
});
142+
// ========== Portfolio: auto-fetch all repos ===========
143+
const owner = 'Bismay-exe';
144+
const grid = document.getElementById('projects-grid');
145+
146+
async function fetchAllRepos(){
147+
let repos = [];
148+
let page = 1;
149+
while(true){
150+
const res = await fetch(`https://api.github.com/users/${owner}/repos?per_page=100&page=${page}`);
151+
if(!res.ok) break;
152+
const chunk = await res.json();
153+
if(chunk.length===0) break;
154+
repos = repos.concat(chunk);
155+
if(chunk.length<100) break; page++;
156+
}
157+
// sort by updated desc
158+
repos.sort((a,b)=> new Date(b.updated_at) - new Date(a.updated_at));
159+
return repos;
160+
}
161+
162+
async function fetchReadme(owner, repo){
163+
try{
164+
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/readme`);
165+
if(!res.ok) return null;
166+
const data = await res.json();
167+
const content = atob(data.content);
168+
const first = content.split('\n\n').find(p=>p.trim().length>0);
169+
return first ? first.replace(/\n/g,' ') : null;
170+
}catch(e){return null}
171+
}
172+
173+
async function fetchTopics(owner, repo){
174+
try{
175+
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/topics`, {headers: {'Accept':'application/vnd.github.mercy-preview+json'}});
176+
if(!res.ok) return [];
177+
const j = await res.json(); return j.names||[];
178+
}catch(e){return []}
179+
}
180+
181+
async function fetchRepo(owner, repo){
182+
try{ const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`); if(!res.ok) return null; return await res.json(); }catch(e){return null}
183+
}
184+
185+
function createCard(repo){
186+
const link = document.createElement('a'); link.className='card card-3d'; link.href=repo.html_url; link.target='_blank'; link.rel='noopener';
187+
const inner = document.createElement('div'); inner.className='card-inner';
188+
const thumb = document.createElement('div'); thumb.className='card-thumb'; thumb.textContent = repo.name;
189+
const title = document.createElement('div'); title.className='card-title'; title.textContent = repo.name.replace(/[-_]/g,' ');
190+
const desc = document.createElement('div'); desc.className='card-desc'; desc.textContent = '⏳ Loading project info...';
191+
const meta = document.createElement('div'); meta.className='card-meta'; meta.textContent = '';
192+
inner.append(thumb,title,desc,meta); link.append(inner); grid.append(link);
193+
194+
// hover tilt
195+
link.addEventListener('mousemove', (e)=>{
196+
const rect = link.getBoundingClientRect(); const x = (e.clientX - rect.left)/rect.width - 0.5; const y = (e.clientY - rect.top)/rect.height - 0.5; const rx = (-y)*12; const ry = x*12; inner.style.transform = `rotateX(${rx}deg) rotateY(${ry}deg) translateZ(8px)`;
197+
});
198+
link.addEventListener('mouseleave', ()=>{ inner.style.transform='none'; });
199+
200+
return {link,inner,thumb,title,desc,meta};
201+
}
202+
203+
(async ()=>{
204+
const repos = await fetchAllRepos();
205+
if(!repos || repos.length===0){ grid.innerHTML='<div style="color:var(--muted)">No public repos found.</div>'; return; }
206+
for(const r of repos){
207+
const cardEls = createCard(r);
208+
// fetch details concurrently
209+
(async ()=>{
210+
const [readme, topics, repoData] = await Promise.all([fetchReadme(owner, r.name), fetchTopics(owner, r.name), fetchRepo(owner, r.name)]);
211+
cardEls.desc.textContent = readme || '⚡ Project details coming soon...';
212+
// badges
213+
if(topics && topics.length){ cardEls.meta.innerHTML = topics.map(t=>`<span class="badge">${t}</span>`).join(' '); }
214+
// stats + live
215+
let stats = '';
216+
if(repoData){ stats += `⭐ ${repoData.stargazers_count} &nbsp; 🍴 ${repoData.forks_count} &nbsp; 🕒 ${new Date(repoData.updated_at).toLocaleDateString()}`; }
217+
// live demo
218+
let live = '';
219+
if(repoData && (repoData.has_pages || repoData.homepage)){
220+
const liveUrl = repoData.homepage ? repoData.homepage : `https://${owner}.github.io/${r.name}/`;
221+
live = ` <a style="margin-left:8px;font-weight:600" href="${liveUrl}" target="_blank" rel="noopener">Live</a>`;
222+
}
223+
if(cardEls.meta.innerHTML) cardEls.meta.innerHTML += '<div style="flex:1"></div>' + `<div class="meta-right">${stats}${live}</div>`; else cardEls.meta.innerHTML = `<div class="meta-right">${stats}${live}</div>`;
224+
})();
225+
}
226+
})();
227+
228+
// help overlay drag (desktop only)
229+
const help = document.getElementById('help-overlay'); let dragging=false,offX=0,offY=0;
230+
help.addEventListener('mousedown',(e)=>{ dragging=true; offX = e.clientX - help.getBoundingClientRect().left; offY = e.clientY - help.getBoundingClientRect().top; help.style.transition='none'; });
231+
window.addEventListener('mousemove',(e)=>{ if(dragging){ help.style.left = (e.clientX - offX) + 'px'; help.style.top = (e.clientY - offY) + 'px'; help.style.right='auto'; } });
232+
window.addEventListener('mouseup',()=>{ if(dragging){ dragging=false; help.style.transition='opacity 0.6s ease'; } });
128233

129234
document.getElementById('year').textContent = new Date().getFullYear();
130235
</script>

0 commit comments

Comments
 (0)