533 lines
26 KiB
HTML
533 lines
26 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||
<title>M. Srikanth | Anti-Gravity Dev Portfolio</title>
|
||
<!-- Google Fonts + Tailwind + Matter.js -->
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
|
||
<!-- Font Awesome Icons -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||
<style>
|
||
/* custom overwrites + glitch + glassmorphic depth */
|
||
body {
|
||
background-color: #050505;
|
||
font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||
overflow-x: hidden;
|
||
}
|
||
@import url('https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,600;14..32,700;14..32,800&display=swap');
|
||
|
||
/* glitch effect for job title */
|
||
.glitch-text {
|
||
position: relative;
|
||
animation: glitch-skew 3s infinite linear alternate-reverse;
|
||
}
|
||
.glitch-text::before,
|
||
.glitch-text::after {
|
||
content: attr(data-text);
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
opacity: 0.8;
|
||
}
|
||
.glitch-text::before {
|
||
color: cyan;
|
||
z-index: -1;
|
||
animation: glitch-1 0.4s infinite ease-in-out alternate;
|
||
}
|
||
.glitch-text::after {
|
||
color: magenta;
|
||
z-index: -2;
|
||
animation: glitch-2 0.3s infinite ease-in-out alternate;
|
||
}
|
||
@keyframes glitch-1 {
|
||
0% { transform: translate(0); }
|
||
100% { transform: translate(-2px, 1px); }
|
||
}
|
||
@keyframes glitch-2 {
|
||
0% { transform: translate(0); }
|
||
100% { transform: translate(2px, -1px); }
|
||
}
|
||
@keyframes glitch-skew {
|
||
0% { transform: skew(0deg); }
|
||
100% { transform: skew(-0.5deg); }
|
||
}
|
||
|
||
/* smooth entrance animation */
|
||
.fade-up {
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
transition: opacity 0.8s cubic-bezier(0.2, 0.9, 0.4, 1.1), transform 0.7s ease;
|
||
}
|
||
.fade-up.appear {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
/* glassmorphism base */
|
||
.glass-card {
|
||
background: rgba(10, 10, 20, 0.55);
|
||
backdrop-filter: blur(12px);
|
||
border: 1px solid rgba(0, 255, 255, 0.18);
|
||
border-radius: 28px;
|
||
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(0, 255, 255, 0.05) inset;
|
||
}
|
||
.glass-card-light {
|
||
background: rgba(15, 15, 30, 0.45);
|
||
backdrop-filter: blur(8px);
|
||
border: 1px solid rgba(100, 100, 255, 0.2);
|
||
border-radius: 24px;
|
||
}
|
||
.neon-border {
|
||
box-shadow: 0 0 6px rgba(0, 255, 255, 0.6);
|
||
}
|
||
/* custom scrollbar */
|
||
::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
::-webkit-scrollbar-track {
|
||
background: #0a0a0a;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
background: cyan;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
/* canvas container for physics arena */
|
||
#skills-arena {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 560px;
|
||
overflow: hidden;
|
||
border-radius: 40px;
|
||
background: radial-gradient(circle at 30% 20%, #0c0c1a, #020205);
|
||
box-shadow: 0 20px 40px -15px rgba(0,0,0,0.8), inset 0 1px 0 rgba(255,255,255,0.05);
|
||
}
|
||
canvas#physicsCanvas {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
cursor: grab;
|
||
}
|
||
canvas#physicsCanvas:active {
|
||
cursor: grabbing;
|
||
}
|
||
.hint-badge {
|
||
position: absolute;
|
||
bottom: 18px;
|
||
right: 22px;
|
||
background: rgba(0, 0, 0, 0.65);
|
||
backdrop-filter: blur(8px);
|
||
padding: 6px 14px;
|
||
border-radius: 60px;
|
||
font-size: 0.8rem;
|
||
letter-spacing: 0.5px;
|
||
border-left: 2px solid cyan;
|
||
z-index: 20;
|
||
pointer-events: none;
|
||
font-weight: 500;
|
||
color: #ccf;
|
||
}
|
||
.skill-pill-floating {
|
||
pointer-events: none; /* just decorative text, actual physics bodies are interactive */
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="text-white selection:bg-cyan-600/50">
|
||
|
||
<main class="max-w-7xl mx-auto px-4 md:px-8 py-6 md:py-10 relative z-10">
|
||
|
||
<!-- Hero Section + Name + Glitch -->
|
||
<div class="flex flex-col md:flex-row justify-between items-start md:items-end gap-4 mb-8 fade-up" id="heroText">
|
||
<div>
|
||
<h1 class="text-6xl sm:text-7xl md:text-8xl font-extrabold tracking-tighter bg-gradient-to-r from-white via-cyan-100 to-purple-200 bg-clip-text text-transparent">M. SRIKANTH</h1>
|
||
<div class="relative inline-block mt-3">
|
||
<h2 class="glitch-text text-2xl md:text-3xl font-semibold text-cyan-300/90" data-text="Junior Software Developer • Creative Technologist">
|
||
Junior Software Developer • Creative Technologist
|
||
</h2>
|
||
</div>
|
||
<div class="flex gap-3 mt-4">
|
||
<span class="px-3 py-1 rounded-full text-xs bg-cyan-500/20 text-cyan-300 border border-cyan-500/40 backdrop-blur-sm">⚡ Full Stack</span>
|
||
<span class="px-3 py-1 rounded-full text-xs bg-purple-500/20 text-purple-300 border border-purple-500/40 backdrop-blur-sm">🧪 Physics UI</span>
|
||
</div>
|
||
</div>
|
||
<div class="text-right">
|
||
<div class="glass-card-light px-5 py-2 rounded-2xl text-sm font-mono">
|
||
<i class="fas fa-map-pin text-cyan-400 mr-1"></i> live & ready for innovation
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ZERO-GRAVITY SKILLS ARENA (main feature) -->
|
||
<div class="my-10 fade-up" id="arenaSection">
|
||
<div class="flex justify-between items-end mb-3 flex-wrap gap-2">
|
||
<h3 class="text-2xl font-bold tracking-tight flex items-center gap-2"><i class="fas fa-gravity text-cyan-400"></i> Zero‑Gravity Skills Arena</h3>
|
||
<div class="text-xs text-cyan-300/70 bg-black/40 px-4 py-1.5 rounded-full backdrop-blur-sm"><i class="fas fa-hand-peace mr-1"></i> grab, drag & throw — zero gravity physics</div>
|
||
</div>
|
||
<div id="skills-arena" class="relative">
|
||
<canvas id="physicsCanvas"></canvas>
|
||
<div class="hint-badge">
|
||
<i class="fas fa-hand-peace mr-1"></i> GRAB + THROW → skills float forever
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- EXPERIENCE & PROJECTS : Glassmorphic Timeline + Cards -->
|
||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mt-16 fade-up" id="expProj">
|
||
<!-- LEFT: WORK EXPERIENCE -->
|
||
<div class="glass-card p-6 md:p-7">
|
||
<div class="flex items-center gap-2 border-b border-cyan-500/40 pb-3 mb-6">
|
||
<i class="fas fa-briefcase text-2xl text-cyan-400"></i>
|
||
<h3 class="text-2xl font-bold">/ work orbit</h3>
|
||
</div>
|
||
<div class="space-y-6">
|
||
<!-- current role: Junior Software Developer -->
|
||
<div class="relative pl-4 border-l-2 border-cyan-400/70">
|
||
<div class="absolute -left-[9px] top-1 w-4 h-4 rounded-full bg-cyan-400 shadow-glow"></div>
|
||
<div class="flex flex-wrap justify-between items-start gap-1">
|
||
<h4 class="text-xl font-semibold flex items-center gap-2">Junior Software Developer <span class="text-sm bg-cyan-800/40 px-2 py-0.5 rounded-full">Jan 2026 – present</span></h4>
|
||
</div>
|
||
<p class="text-cyan-200/80 text-sm font-mono">Metetroncub Software Solutions</p>
|
||
<ul class="mt-2 space-y-1 text-gray-300 text-sm list-disc list-inside">
|
||
<li>🚀 Production-level web app deployed, scaling backend services.</li>
|
||
<li>⚙️ ERP development using Odoo & Frappe — custom module logic.</li>
|
||
<li>🧩 Implemented business workflows & enterprise solutions.</li>
|
||
</ul>
|
||
</div>
|
||
<!-- Internship: Flutter Developer Intern -->
|
||
<div class="relative pl-4 border-l-2 border-purple-400/50">
|
||
<div class="absolute -left-[9px] top-1 w-4 h-4 rounded-full bg-purple-400"></div>
|
||
<div class="flex flex-wrap justify-between">
|
||
<h4 class="text-lg font-semibold">Flutter Developer Intern</h4>
|
||
<span class="text-xs text-purple-300">May 2025 (1 month)</span>
|
||
</div>
|
||
<p class="text-purple-200/80 text-sm">Doozy Info Software Solutions</p>
|
||
<ul class="mt-1 text-gray-300 text-xs space-y-1 list-disc ml-4">
|
||
<li>📱 Money Tracking System (daily expense & income manager)</li>
|
||
<li>🏋️ Gym Management Mobile App (in progress, Flutter state management)</li>
|
||
</ul>
|
||
</div>
|
||
<div class="text-xs text-gray-400 italic mt-2 flex gap-4">
|
||
<span><i class="far fa-check-circle text-cyan-500"></i> 2+ years coding intensity</span>
|
||
<span><i class="fas fa-microchip"></i> full-stack & embedded logic</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- RIGHT: FEATURED PROJECTS (glass cards) -->
|
||
<div class="glass-card p-6 md:p-7">
|
||
<div class="flex items-center gap-2 border-b border-purple-500/40 pb-3 mb-6">
|
||
<i class="fas fa-cubes text-2xl text-purple-400"></i>
|
||
<h3 class="text-2xl font-bold">/ projects nebula</h3>
|
||
</div>
|
||
<div class="space-y-5">
|
||
<div class="bg-black/20 rounded-2xl p-4 transition-all hover:scale-[1.01] duration-300 border border-white/5">
|
||
<div class="flex justify-between items-start">
|
||
<h4 class="font-bold text-lg flex items-center gap-2"><i class="fas fa-car-side text-cyan-400"></i> Rental Platform</h4>
|
||
<span class="text-xs bg-cyan-900/40 px-2 py-0.5 rounded-full">dreamscars.in</span>
|
||
</div>
|
||
<p class="text-gray-300 text-sm mt-1">Full CRUD car rental management • real-time vehicle listings • responsive frontend (HTML/CSS/JS, MySQL)</p>
|
||
</div>
|
||
<div class="bg-black/20 rounded-2xl p-4 transition-all hover:scale-[1.01] duration-300 border border-white/5">
|
||
<div class="flex justify-between items-start">
|
||
<h4 class="font-bold text-lg"><i class="fas fa-globe text-purple-300"></i> Car Rental Platform</h4>
|
||
<span class="text-xs bg-purple-900/40 px-2 py-0.5 rounded-full">rabbihram.com</span>
|
||
</div>
|
||
<p class="text-gray-300 text-sm mt-1">Full‑stack Flask + MongoDB • booking workflows, availability tracking & realtime sync.</p>
|
||
</div>
|
||
<div class="bg-black/20 rounded-2xl p-4 transition-all hover:scale-[1.01] duration-300 border border-white/5">
|
||
<div class="flex justify-between items-start">
|
||
<h4 class="font-bold text-lg"><i class="fas fa-robot text-cyan-300"></i> Website Embedded Chatbot</h4>
|
||
<span class="text-xs bg-cyan-900/40 px-2 py-0.5 rounded-full">buildzo.info</span>
|
||
</div>
|
||
<p class="text-gray-300 text-sm mt-1">NLP rule‑based assistant • reduced manual support • integrated frontend + backend logic for seamless UX.</p>
|
||
</div>
|
||
<div class="text-sm text-gray-400 border-t border-gray-700/50 pt-3 mt-2 flex flex-wrap gap-3">
|
||
<span><i class="fab fa-python"></i> Django mini-projects: ToDo, BookSky, Library Mgmt</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Education & Certifications + contact hybrid row -->
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-10 fade-up" id="eduContact">
|
||
<div class="glass-card-light col-span-2 p-5">
|
||
<div class="flex items-center gap-3 mb-3">
|
||
<i class="fas fa-graduation-cap text-cyan-400 text-xl"></i>
|
||
<h3 class="text-xl font-bold">academic trajectory</h3>
|
||
</div>
|
||
<div class="flex flex-wrap gap-6">
|
||
<div><span class="text-cyan-300 font-semibold">M.Sc Data Science (Pursuing)</span><br>Nehru Arts & Science College (2024-2026) | CGPA 7.5</div>
|
||
<div><span class="text-cyan-300 font-semibold">B.Sc Computer Science</span><br>KPR College (2020-2023) | CGPA 7.8</div>
|
||
</div>
|
||
<div class="mt-4 pt-3 border-t border-white/10">
|
||
<p class="text-sm text-gray-300"><i class="fas fa-certificate text-purple-400 mr-2"></i> Master Program: Full Stack Dev (Python, Django, REST APIs) – Livewire, 2024</p>
|
||
</div>
|
||
</div>
|
||
<!-- Contact magnetic button & socials -->
|
||
<div class="glass-card-light p-5 flex flex-col items-start justify-between">
|
||
<div>
|
||
<i class="fas fa-paper-plane text-cyan-400 text-xl mb-2 block"></i>
|
||
<h3 class="text-lg font-semibold">gravitational contact</h3>
|
||
<p class="text-sm text-gray-300 mt-1 break-all">srikanthm1523@gmail.com</p>
|
||
</div>
|
||
<div class="flex gap-4 mt-4 w-full">
|
||
<a href="mailto:srikanthm1523@gmail.com" class="magnetic-btn w-full text-center bg-cyan-600/30 hover:bg-cyan-500/60 transition-all backdrop-blur-sm py-3 rounded-xl font-medium flex items-center justify-center gap-2 border border-cyan-400/40 group">
|
||
<i class="fas fa-envelope"></i> Email me
|
||
</a>
|
||
<a href="#" target="_blank" class="magnetic-btn w-full text-center bg-purple-600/30 hover:bg-purple-500/60 transition-all backdrop-blur-sm py-3 rounded-xl font-medium flex items-center justify-center gap-2 border border-purple-400/40">
|
||
<i class="fab fa-linkedin"></i> LinkedIn
|
||
</a>
|
||
</div>
|
||
<div class="text-[11px] text-gray-500 mt-4">✨ based on physics — drag skills around ✨</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- tiny footer -->
|
||
<div class="text-center text-gray-500 text-xs py-8 mt-6 border-t border-white/5 fade-up">
|
||
⚡ zero‑gravity interactive arena • matter.js physics • anti‑gravity skills • designed with glassmorphism
|
||
</div>
|
||
</main>
|
||
|
||
<script>
|
||
// --- EXTRACTED DATA: core skills as floating blocks (pill/block style)
|
||
const SKILL_SET = [
|
||
"Python", "JavaScript", "React.js", "Next.js", "Flask", "Django",
|
||
"Tailwind CSS", "MySQL", "PostgreSQL", "MongoDB", "Git", "Matter.js"
|
||
];
|
||
|
||
// Matter.js globals
|
||
let Engine = Matter.Engine;
|
||
let World = Matter.World;
|
||
let Bodies = Matter.Bodies;
|
||
let Mouse = Matter.Mouse;
|
||
let MouseConstraint = Matter.MouseConstraint;
|
||
let Body = Matter.Body;
|
||
let Vector = Matter.Vector;
|
||
let Events = Matter.Events;
|
||
|
||
let engine, world, mouseConstraint, canvasElem, ctx;
|
||
let animationId;
|
||
let activeBodies = [];
|
||
let currentWidth = 0, currentHeight = 0;
|
||
let arenaContainer = document.getElementById('skills-arena');
|
||
let canvas;
|
||
let isPhysicsInit = false;
|
||
|
||
// custom drawing: render pills + labels + 3D gradients
|
||
function drawPhysics() {
|
||
if (!ctx || !engine) return;
|
||
ctx.clearRect(0, 0, currentWidth, currentHeight);
|
||
// draw subtle grid / energy lines
|
||
ctx.save();
|
||
for (let body of activeBodies) {
|
||
const pos = body.position;
|
||
const angle = body.angle;
|
||
const w = body.bounds.max.x - body.bounds.min.x;
|
||
const h = body.bounds.max.y - body.bounds.min.y;
|
||
const halfW = w / 2;
|
||
const halfH = h / 2;
|
||
ctx.save();
|
||
ctx.translate(pos.x, pos.y);
|
||
ctx.rotate(angle);
|
||
// gradient for 3D looking pill (glossy)
|
||
const grad = ctx.createLinearGradient(-halfW * 0.6, -halfH * 0.6, halfW * 0.6, halfH * 0.6);
|
||
grad.addColorStop(0, '#0ae0ff');
|
||
grad.addColorStop(0.5, '#7c3aed');
|
||
grad.addColorStop(1, '#2dd4bf');
|
||
ctx.shadowBlur = 14;
|
||
ctx.shadowColor = 'rgba(0, 230, 250, 0.7)';
|
||
ctx.fillStyle = grad;
|
||
// rounded rectangle (pill shape) radius 20px
|
||
const radius = 20;
|
||
ctx.beginPath();
|
||
ctx.moveTo(-halfW + radius, -halfH);
|
||
ctx.lineTo(halfW - radius, -halfH);
|
||
ctx.quadraticCurveTo(halfW, -halfH, halfW, -halfH + radius);
|
||
ctx.lineTo(halfW, halfH - radius);
|
||
ctx.quadraticCurveTo(halfW, halfH, halfW - radius, halfH);
|
||
ctx.lineTo(-halfW + radius, halfH);
|
||
ctx.quadraticCurveTo(-halfW, halfH, -halfW, halfH - radius);
|
||
ctx.lineTo(-halfW, -halfH + radius);
|
||
ctx.quadraticCurveTo(-halfW, -halfH, -halfW + radius, -halfH);
|
||
ctx.closePath();
|
||
ctx.fill();
|
||
// neon border
|
||
ctx.strokeStyle = '#aaffff';
|
||
ctx.lineWidth = 1.5;
|
||
ctx.stroke();
|
||
ctx.shadowBlur = 0;
|
||
// draw text label
|
||
ctx.font = `bold ${Math.max(14, Math.min(18, halfW * 0.28))}px "Inter", system-ui`;
|
||
ctx.fillStyle = '#ffffff';
|
||
ctx.shadowBlur = 2;
|
||
ctx.shadowColor = 'black';
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
ctx.fillText(body.label, 0, 0);
|
||
ctx.restore();
|
||
}
|
||
// tiny glow effect for walls ?? optional
|
||
ctx.restore();
|
||
}
|
||
|
||
function initPhysics(containerWidth, containerHeight) {
|
||
if (engine) {
|
||
// cleanup previous engine and world
|
||
if (mouseConstraint) World.remove(world, mouseConstraint);
|
||
Events.off(engine);
|
||
World.clear(world, false);
|
||
Engine.clear(engine);
|
||
cancelAnimationFrame(animationId);
|
||
}
|
||
engine = Engine.create();
|
||
world = engine.world;
|
||
engine.gravity = { x: 0, y: 0, scale: 0 }; // ZERO GRAVITY !
|
||
|
||
// boundaries (walls): left, right, top, bottom using static bodies
|
||
const wallThickness = 30;
|
||
const walls = [
|
||
Bodies.rectangle(-wallThickness/2, containerHeight/2, wallThickness, containerHeight, { isStatic: true, restitution: 0.9, friction: 0.1 }),
|
||
Bodies.rectangle(containerWidth + wallThickness/2, containerHeight/2, wallThickness, containerHeight, { isStatic: true, restitution: 0.9, friction: 0.1 }),
|
||
Bodies.rectangle(containerWidth/2, -wallThickness/2, containerWidth, wallThickness, { isStatic: true, restitution: 0.9, friction: 0.1 }),
|
||
Bodies.rectangle(containerWidth/2, containerHeight + wallThickness/2, containerWidth, wallThickness, { isStatic: true, restitution: 0.9, friction: 0.1 })
|
||
];
|
||
World.add(world, walls);
|
||
|
||
// create floating skill blocks (rounded rectangles matter bodies)
|
||
const bodyWidth = 98;
|
||
const bodyHeight = 46;
|
||
activeBodies = [];
|
||
const margin = 50;
|
||
for (let i = 0; i < SKILL_SET.length; i++) {
|
||
let x = margin + Math.random() * (containerWidth - margin * 2);
|
||
let y = margin + Math.random() * (containerHeight - margin * 2);
|
||
let body = Bodies.rectangle(x, y, bodyWidth, bodyHeight, {
|
||
restitution: 0.85,
|
||
friction: 0.05,
|
||
frictionAir: 0.001,
|
||
density: 0.002,
|
||
label: SKILL_SET[i],
|
||
render: { visible: false } // we custom draw
|
||
});
|
||
// random small angular velocity to make it spin
|
||
Body.setAngularVelocity(body, (Math.random() - 0.5) * 0.03);
|
||
Body.setVelocity(body, { x: (Math.random() - 0.5) * 1.5, y: (Math.random() - 0.5) * 1.5 });
|
||
activeBodies.push(body);
|
||
}
|
||
World.add(world, activeBodies);
|
||
|
||
// mouse interaction: drag and throw (zero gravity still applies momentum)
|
||
let mouse = Mouse.create(canvas);
|
||
mouseConstraint = MouseConstraint.create(engine, {
|
||
mouse: mouse,
|
||
constraint: { stiffness: 0.2, render: { visible: false } }
|
||
});
|
||
World.add(world, mouseConstraint);
|
||
// keep mouse inside canvas region
|
||
canvas.addEventListener('mouseleave', () => {
|
||
if (mouseConstraint && mouseConstraint.mouse) {
|
||
mouseConstraint.mouse.button = -1;
|
||
}
|
||
});
|
||
|
||
// run engine loop: update + redraw
|
||
function animate() {
|
||
Engine.update(engine, 16.6);
|
||
drawPhysics();
|
||
animationId = requestAnimationFrame(animate);
|
||
}
|
||
animate();
|
||
isPhysicsInit = true;
|
||
}
|
||
|
||
function resizeAndResetArena() {
|
||
if (!arenaContainer) return;
|
||
const rect = arenaContainer.getBoundingClientRect();
|
||
let width = rect.width;
|
||
let height = rect.height;
|
||
if (width < 300) width = 300;
|
||
if (height < 300) height = 480;
|
||
currentWidth = width;
|
||
currentHeight = height;
|
||
canvas.width = width;
|
||
canvas.height = height;
|
||
canvas.style.width = `${width}px`;
|
||
canvas.style.height = `${height}px`;
|
||
ctx = canvas.getContext('2d');
|
||
if (engine) {
|
||
// reinitialize physics with new container size
|
||
initPhysics(width, height);
|
||
} else {
|
||
initPhysics(width, height);
|
||
}
|
||
}
|
||
|
||
// on load setup canvas & matter
|
||
window.addEventListener('load', () => {
|
||
canvas = document.getElementById('physicsCanvas');
|
||
if (!canvas) return;
|
||
arenaContainer = document.getElementById('skills-arena');
|
||
const resizeObserver = new ResizeObserver(() => {
|
||
if (arenaContainer) {
|
||
resizeAndResetArena();
|
||
}
|
||
});
|
||
resizeObserver.observe(arenaContainer);
|
||
resizeAndResetArena();
|
||
window.addEventListener('resize', () => {
|
||
setTimeout(() => {
|
||
if (arenaContainer) resizeAndResetArena();
|
||
}, 100);
|
||
});
|
||
|
||
// FADE-UP intersection observer for entry animations
|
||
const fadeElements = document.querySelectorAll('.fade-up');
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('appear');
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, { threshold: 0.1 });
|
||
fadeElements.forEach(el => observer.observe(el));
|
||
|
||
// force hero text appear quickly
|
||
setTimeout(() => {
|
||
document.querySelectorAll('.fade-up').forEach(el => {
|
||
if (el.getBoundingClientRect().top < window.innerHeight - 100) el.classList.add('appear');
|
||
});
|
||
}, 100);
|
||
});
|
||
|
||
// extra: magnetic button simple hover effect
|
||
const btns = document.querySelectorAll('.magnetic-btn');
|
||
btns.forEach(btn => {
|
||
btn.addEventListener('mousemove', (e) => {
|
||
const rect = btn.getBoundingClientRect();
|
||
const x = e.clientX - rect.left;
|
||
const y = e.clientY - rect.top;
|
||
const centerX = rect.width/2;
|
||
const centerY = rect.height/2;
|
||
const moveX = (x - centerX) * 0.15;
|
||
const moveY = (y - centerY) * 0.15;
|
||
btn.style.transform = `translate(${moveX}px, ${moveY}px)`;
|
||
});
|
||
btn.addEventListener('mouseleave', () => {
|
||
btn.style.transform = 'translate(0px, 0px)';
|
||
});
|
||
});
|
||
|
||
// Fix linkedin placeholder (update actual link from resume: srikanth1523)
|
||
const linkedBtn = document.querySelector('a[href="#"]');
|
||
if (linkedBtn && linkedBtn.innerHTML.includes('LinkedIn')) {
|
||
linkedBtn.href = 'https://www.linkedin.com/in/srikanth1523/';
|
||
linkedBtn.target = '_blank';
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |