Created
April 27, 2025 02:44
-
-
Save frumbert/02684580a0bd1bc48c83cdcb68bded6a to your computer and use it in GitHub Desktop.
Sleepy xEyes in modern javascript
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>Spy</title> | |
<style> | |
.demo{background:#69c;padding:10px;display:flex;justify-content:flex-end;gap:5px} | |
</style> | |
</head> | |
<body> | |
<main class='demo' /> | |
<script> | |
class EyeSpy { | |
constructor (container, options) { | |
this.container = container; | |
this.settings = Object.assign({ | |
eyes: 2, | |
iris: 16, | |
pupil: 4, | |
fg: 'white', | |
bg: '#69c', | |
delay: 5000, //ms | |
cssName: 'eyespy' | |
}, options); | |
this.maxDist = this.settings.iris - this.settings.pupil; | |
for (let i=0; i<this.settings.eyes; i++) { | |
this.container.innerHTML += ` | |
<div style='--delay:${this.settings.delay}ms;width:${this.settings.iris*2}px;height:${this.settings.iris*2}px;border-radius:${this.settings.iris}px;border:1px solid ${this.settings.fg};background:${this.settings.bg};' class='${this.settings.cssName}'> | |
<span style='width:${this.settings.pupil*2}px;height:${this.settings.pupil*2}px;border-radius:${this.settings.pupil}px;background:${this.settings.fg};' data-pupil /> | |
</div>`; | |
} | |
this.pupils = container.querySelectorAll('[data-pupil]'); | |
document.addEventListener('mousemove',this.move.bind(this)); | |
if (!document.querySelector('[data-eye-style]')) { | |
const style = document.createElement('style'); | |
style.dataset.eyeStyle = !0; | |
style.textContent = ` | |
.${this.settings.cssName} {display:inline-block;position:relative;font-size:0;transition-delay:0;transition-duration:.2s;clip-path:ellipse(50% 50% at 50% 50%); | |
&.snooze{border-color:red;clip-path:ellipse(50% 0 at 50% 100%);transition:clip-path 2s;transition-timing-function:cubic-bezier(0,1,.23,-0.2);transition-delay:var(--delay);} | |
>span {position:absolute;top:50%;left:50%;}}`; | |
document.head.appendChild(style); | |
} | |
} | |
snooze(eye) { | |
eye.classList.add('snooze'); | |
} | |
move(e) { | |
for (const el of this.pupils) { | |
const eye = el.parentNode; | |
eye.classList.remove('snooze'); | |
const midx = this.pos(eye, true) + this.settings.iris; | |
const midy = this.pos(eye, false) + this.settings.iris; | |
let distX = e.clientX + document.documentElement.scrollLeft - midx; | |
let distY = e.clientY + document.documentElement.scrollTop - midy; | |
const dist = Math.sqrt(Math.pow(distX, 2) + Math.pow(distY, 2)); | |
if (dist > this.maxDist) { | |
const scale = this.maxDist / dist; | |
distX *= scale; | |
distY *= scale; | |
} | |
el.style.left = parseInt(distX + this.settings.iris - this.settings.pupil) + "px"; | |
el.style.top = parseInt(distY + this.settings.iris - this.settings.pupil) + "px"; | |
setTimeout(this.snooze, this.settings.delay/2, eye); | |
} | |
} | |
pos(el,left) { | |
let val = 0; | |
while (el != null) { | |
val += el["offset" + (left ? "Left" : "Top")]; | |
el = el.offsetParent; | |
} | |
return val; | |
} | |
} | |
new EyeSpy(document.querySelector('main')); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment