Created
May 14, 2025 13:11
-
-
Save zinefer/d77c0d1987ac61b3fd679ab0a0bcdc92 to your computer and use it in GitHub Desktop.
Eyes Animation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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