Skip to content

Instantly share code, notes, and snippets.

@zinefer
Created May 14, 2025 13:11
Show Gist options
  • Save zinefer/d77c0d1987ac61b3fd679ab0a0bcdc92 to your computer and use it in GitHub Desktop.
Save zinefer/d77c0d1987ac61b3fd679ab0a0bcdc92 to your computer and use it in GitHub Desktop.
Eyes Animation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pixar Cars-like Eyes Animation</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #333;
font-family: Arial, sans-serif;
}
#container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
#eyes-container {
display: flex;
justify-content: center;
gap: 40px;
margin-bottom: 20px;
}
.eye-wrapper {
position: relative;
}
.eye {
width: 180px;
height: 140px;
background-color: white;
border-radius: 100px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5), inset 0 0 15px rgba(0, 0, 0, 0.2);
overflow: hidden;
position: relative;
}
.pupil {
width: 70px;
height: 70px;
background: radial-gradient(circle at 40% 40%, #5a9cf2 0%, #1c57b5 55%, #0c3b96 75%);
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
}
.pupil::after {
content: '';
position: absolute;
top: 15%;
left: 15%;
width: 25%;
height: 25%;
background-color: white;
border-radius: 50%;
opacity: 0.8;
}
.eyelid {
position: absolute;
width: 100%;
height: 50%;
background-color: #555;
z-index: 10;
transition: transform 0.3s;
}
.eyelid.top {
top: 0;
border-bottom-left-radius: 100px;
border-bottom-right-radius: 100px;
transform: translateY(-100%);
}
.eyelid.bottom {
bottom: 0;
border-top-left-radius: 100px;
border-top-right-radius: 100px;
transform: translateY(100%);
}
.eyebrow {
position: absolute;
width: 120px;
height: 20px;
background-color: #333;
top: -30px;
left: 50%;
transform: translateX(-50%);
border-radius: 10px;
transition: transform 0.3s ease;
}
.controls {
display: flex;
flex-direction: column;
gap: 10px;
background-color: rgba(255, 255, 255, 0.1);
padding: 15px;
border-radius: 10px;
}
.control-row {
display: flex;
align-items: center;
gap: 10px;
color: white;
}
button {
background-color: #4a89dc;
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: #5a97e8;
}
button:active {
background-color: #3b7bd4;
}
</style>
</head>
<body>
<div id="container">
<div id="eyes-container">
<!-- Eyes will be generated here -->
</div>
<div class="controls">
<div class="control-row">
<button id="add-eye">Add Eye</button>
<button id="remove-eye">Remove Eye</button>
</div>
<div class="control-row">
<button id="blink">Blink</button>
<button id="squint">Squint</button>
<button id="look-up">Look Up</button>
</div>
<div class="control-row">
<button id="look-down">Look Down</button>
<button id="surprise">Surprise</button>
<button id="angry">Angry</button>
</div>
</div>
</div>
<script>
// State
const state = {
eyeCount: 2,
maxEyes: 10,
minEyes: 1,
isSquinting: false,
isLookingUp: false,
isLookingDown: false,
isSurprised: false,
isAngry: false,
blinkInterval: null,
mouseX: window.innerWidth / 2,
mouseY: window.innerHeight / 2
};
// Generate initial eyes
function generateEyes() {
const eyesContainer = document.getElementById('eyes-container');
eyesContainer.innerHTML = '';
for (let i = 0; i < state.eyeCount; i++) {
const eyeWrapper = document.createElement('div');
eyeWrapper.className = 'eye-wrapper';
const eyebrow = document.createElement('div');
eyebrow.className = 'eyebrow';
const eye = document.createElement('div');
eye.className = 'eye';
const topEyelid = document.createElement('div');
topEyelid.className = 'eyelid top';
const bottomEyelid = document.createElement('div');
bottomEyelid.className = 'eyelid bottom';
const pupil = document.createElement('div');
pupil.className = 'pupil';
eye.appendChild(pupil);
eye.appendChild(topEyelid);
eye.appendChild(bottomEyelid);
eyeWrapper.appendChild(eyebrow);
eyeWrapper.appendChild(eye);
eyesContainer.appendChild(eyeWrapper);
}
updatePupilPositions();
startBlinking();
}
// Update pupils to follow mouse
function updatePupilPositions() {
const eyes = document.querySelectorAll('.eye');
eyes.forEach(eye => {
const pupil = eye.querySelector('.pupil');
const eyeRect = eye.getBoundingClientRect();
const eyeCenterX = eyeRect.left + eyeRect.width / 2;
const eyeCenterY = eyeRect.top + eyeRect.height / 2;
// Calculate angle between eye center and mouse
const angle = Math.atan2(state.mouseY - eyeCenterY, state.mouseX - eyeCenterX);
// Maximum distance the pupil can move from center (in px)
const maxDistance = Math.min(eyeRect.width, eyeRect.height) * 0.2;
// Calculate new pupil position
let pupilX = Math.cos(angle) * maxDistance;
let pupilY = Math.sin(angle) * maxDistance;
// Add modifiers based on state
if (state.isLookingUp) {
pupilY -= 15;
} else if (state.isLookingDown) {
pupilY += 15;
}
// Apply position
pupil.style.transform = `translate(calc(-50% + ${pupilX}px), calc(-50% + ${pupilY}px))`;
// Apply other states
if (state.isSquinting) {
eye.querySelector('.eyelid.top').style.transform = 'translateY(-60%)';
eye.querySelector('.eyelid.bottom').style.transform = 'translateY(60%)';
} else if (state.isSurprised) {
eye.querySelector('.eyelid.top').style.transform = 'translateY(-110%)';
eye.querySelector('.eyelid.bottom').style.transform = 'translateY(110%)';
} else {
eye.querySelector('.eyelid.top').style.transform = 'translateY(-100%)';
eye.querySelector('.eyelid.bottom').style.transform = 'translateY(100%)';
}
// Apply eyebrow state
const eyebrow = eye.parentElement.querySelector('.eyebrow');
if (state.isAngry) {
eyebrow.style.transform = 'translateX(-50%) rotate(-15deg)';
eyebrow.style.left = '40%';
} else if (state.isSurprised) {
eyebrow.style.transform = 'translateX(-50%) translateY(-10px)';
eyebrow.style.left = '50%';
} else {
eyebrow.style.transform = 'translateX(-50%)';
eyebrow.style.left = '50%';
}
});
}
// Blink animation
function blink() {
const topEyelids = document.querySelectorAll('.eyelid.top');
const bottomEyelids = document.querySelectorAll('.eyelid.bottom');
// Close eyes
topEyelids.forEach(lid => lid.style.transform = 'translateY(0)');
bottomEyelids.forEach(lid => lid.style.transform = 'translateY(0)');
// Open eyes after a short delay
setTimeout(() => {
if (state.isSquinting) {
topEyelids.forEach(lid => lid.style.transform = 'translateY(-60%)');
bottomEyelids.forEach(lid => lid.style.transform = 'translateY(60%)');
} else if (state.isSurprised) {
topEyelids.forEach(lid => lid.style.transform = 'translateY(-110%)');
bottomEyelids.forEach(lid => lid.style.transform = 'translateY(110%)');
} else {
topEyelids.forEach(lid => lid.style.transform = 'translateY(-100%)');
bottomEyelids.forEach(lid => lid.style.transform = 'translateY(100%)');
}
}, 150);
}
// Start automatic blinking
function startBlinking() {
if (state.blinkInterval) {
clearInterval(state.blinkInterval);
}
state.blinkInterval = setInterval(() => {
// Random time between 2-6 seconds
const nextBlinkDelay = 2000 + Math.random() * 4000;
setTimeout(blink, nextBlinkDelay);
}, 6000);
}
// Event listeners
document.addEventListener('mousemove', (e) => {
state.mouseX = e.clientX;
state.mouseY = e.clientY;
updatePupilPositions();
});
document.getElementById('add-eye').addEventListener('click', () => {
if (state.eyeCount < state.maxEyes) {
state.eyeCount++;
generateEyes();
}
});
document.getElementById('remove-eye').addEventListener('click', () => {
if (state.eyeCount > state.minEyes) {
state.eyeCount--;
generateEyes();
}
});
document.getElementById('blink').addEventListener('click', blink);
document.getElementById('squint').addEventListener('click', () => {
state.isSquinting = !state.isSquinting;
state.isSurprised = false;
updatePupilPositions();
});
document.getElementById('look-up').addEventListener('click', () => {
state.isLookingUp = !state.isLookingUp;
state.isLookingDown = false;
updatePupilPositions();
});
document.getElementById('look-down').addEventListener('click', () => {
state.isLookingDown = !state.isLookingDown;
state.isLookingUp = false;
updatePupilPositions();
});
document.getElementById('surprise').addEventListener('click', () => {
state.isSurprised = !state.isSurprised;
state.isSquinting = false;
updatePupilPositions();
});
document.getElementById('angry').addEventListener('click', () => {
state.isAngry = !state.isAngry;
updatePupilPositions();
});
// Touch support for mobile
document.addEventListener('touchmove', (e) => {
if (e.touches.length > 0) {
state.mouseX = e.touches[0].clientX;
state.mouseY = e.touches[0].clientY;
updatePupilPositions();
e.preventDefault();
}
});
// Initialize
generateEyes();
// Make eyes look somewhere interesting initially
setTimeout(() => {
state.mouseX = window.innerWidth * 0.75;
state.mouseY = window.innerHeight * 0.25;
updatePupilPositions();
}, 500);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment