β Volver
Cinematic Mascot Login
Production-level animated login with SVG mascot, smooth physics-like eye tracking, organic blinking, and realistic hand motion
UI
Preview
<section class="w-full min-h-screen bg-[#F5F6F7] flex items-center justify-center px-4">
<div class="w-full max-w-sm bg-white border rounded-2xl p-8 shadow-sm text-center">
<!-- MASCOT -->
<div id="mascot" class="relative w-32 h-32 mx-auto mb-6">
<svg viewBox="0 0 200 200" class="w-full h-full">
<!-- Head -->
<circle cx="100" cy="100" r="90" fill="#FFD54F"/>
<!-- Eyes -->
<g id="eyes">
<ellipse cx="70" cy="90" rx="18" ry="20" fill="white"/>
<ellipse cx="130" cy="90" rx="18" ry="20" fill="white"/>
<circle id="pupilL" cx="70" cy="90" r="7" fill="black"/>
<circle id="pupilR" cx="130" cy="90" r="7" fill="black"/>
</g>
</svg>
<!-- HANDS -->
<div class="absolute inset-0 flex justify-between px-4 pointer-events-none">
<div id="leftHand"
class="w-10 h-10 bg-yellow-400 rounded-full transition-all duration-700 ease-[cubic-bezier(.34,1.56,.64,1)] transform translate-y-28">
</div>
<div id="rightHand"
class="w-10 h-10 bg-yellow-400 rounded-full transition-all duration-700 ease-[cubic-bezier(.34,1.56,.64,1)] transform translate-y-28">
</div>
</div>
</div>
<h2 class="text-lg font-semibold text-gray-900">Welcome back</h2>
<form class="mt-6 space-y-4">
<input type="email"
placeholder="Email"
class="w-full px-4 py-2 border rounded-xl focus:ring-2 focus:ring-black outline-none"/>
<div class="relative">
<input id="password"
type="password"
placeholder="Password"
class="w-full px-4 py-2 border rounded-xl focus:ring-2 focus:ring-black outline-none"/>
<button type="button" id="toggle"
class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500">
π
</button>
</div>
<button class="w-full py-2 bg-black text-white rounded-xl hover:scale-105 transition">
Login
</button>
</form>
</div>
<script>
const container = document.getElementById("mascot");
const pupilL = document.getElementById("pupilL");
const pupilR = document.getElementById("pupilR");
const leftHand = document.getElementById("leftHand");
const rightHand = document.getElementById("rightHand");
const password = document.getElementById("password");
const toggle = document.getElementById("toggle");
let visible = false;
// π EYES FOLLOW (solo dentro del contenedor)
container.addEventListener("mousemove", (e) => {
const rect = container.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
function moveEye(cx, cy, pupil) {
const dx = x - cx;
const dy = y - cy;
const angle = Math.atan2(dy, dx);
const radius = 6;
const targetX = cx + Math.cos(angle) * radius;
const targetY = cy + Math.sin(angle) * radius;
pupil.setAttribute("cx", targetX);
pupil.setAttribute("cy", targetY);
}
moveEye(70, 90, pupilL);
moveEye(130, 90, pupilR);
});
// π BLINK ORGΓNICO
function blink() {
pupilL.setAttribute("r", "1");
pupilR.setAttribute("r", "1");
setTimeout(() => {
pupilL.setAttribute("r", "7");
pupilR.setAttribute("r", "7");
}, 120);
const next = Math.random() * 4000 + 2000;
setTimeout(blink, next);
}
blink();
// π COVER EYES (suave)
password.addEventListener("focus", () => {
leftHand.style.transform = "translateY(40px) translateX(-10px)";
rightHand.style.transform = "translateY(40px) translateX(10px)";
pupilL.setAttribute("opacity", "0");
pupilR.setAttribute("opacity", "0");
});
// π RESET
password.addEventListener("blur", () => {
if (!visible) {
leftHand.style.transform = "translateY(110px)";
rightHand.style.transform = "translateY(110px)";
pupilL.setAttribute("opacity", "1");
pupilR.setAttribute("opacity", "1");
}
});
// π TOGGLE PEEK (UN OJO)
toggle.addEventListener("click", () => {
visible = !visible;
password.type = visible ? "text" : "password";
if (visible) {
leftHand.style.transform = "translateY(40px) translateX(-25px)";
rightHand.style.transform = "translateY(40px) translateX(10px)";
pupilL.setAttribute("opacity", "1");
pupilR.setAttribute("opacity", "0");
} else {
leftHand.style.transform = "translateY(40px) translateX(-10px)";
rightHand.style.transform = "translateY(40px) translateX(10px)";
pupilL.setAttribute("opacity", "0");
pupilR.setAttribute("opacity", "0");
}
});
</script>
</section>