.face
.face__eyes
.eye.eye--left
.eye.eye--right
.face__mouth
View Compiled
*
box-sizing border-box
:root
--height 20vmin
--width 85vmin
--feature 15vmin
--stroke hsl(0, 0%, 10%)
--white hsl(0, 0%, 98%)
--red hsl(10, 90%, 50%)
--bg hsl(295, 80%, 80%)
body
min-height 100vh
display grid
place-items center
background var(--bg)
.face
height var(--height)
position relative
width var(--width)
&__eyes
position absolute
top 0
left 0
right 0
height var(--feature)
transform translate(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px))
&__mouth
height var(--feature)
width calc(var(--feature) * 1.5)
background var(--stroke)
border-radius 50%
position absolute
left 50%
bottom 0
transform translateX(-50%) translate(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px))
clip-path inset(50% 0 0 0)
overflow hidden
&:after
content ''
position absolute
height 50%
width 50%
background var(--red)
top 75%
left 60%
border-radius 50%
.eye
position absolute
height var(--feature)
width var(--feature)
background var(--stroke)
border-radius 50%
top 0
overflow hidden
&:after
content ''
position absolute
top 15%
left 20%
background var(--white)
border-radius 50%
height 20%
width 20%
&--left
left 0
&--right
right 0
View Compiled
const mapRange = (inputLower, inputUpper, outputLower, outputUpper) => {
const INPUT_RANGE = inputUpper - inputLower
const OUTPUT_RANGE = outputUpper - outputLower
return value => outputLower + (((value - inputLower) / INPUT_RANGE) * OUTPUT_RANGE || 0)
}
const FACE = document.querySelector('.face')
// Alternatively
// const X_MAPPER = mapRange(0, window.innerWidth, -10, 10)
const BOUNDS = 20
const update = ({ x, y }) => {
const POS_X = mapRange(0, window.innerWidth, -BOUNDS, BOUNDS)(x)
const POS_Y = mapRange(0, window.innerHeight, -BOUNDS, BOUNDS)(y)
FACE.style.setProperty('--x', POS_X)
FACE.style.setProperty('--y', POS_Y)
}
document.addEventListener('pointermove', update)
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.