Made using SVG morphing technique. Inspired by a Dribbble shot: https://dribbble.com/shots/7190248-Toggle
A Pen by Kiarash Zarinmehr on CodePen.
Made using SVG morphing technique. Inspired by a Dribbble shot: https://dribbble.com/shots/7190248-Toggle
A Pen by Kiarash Zarinmehr on CodePen.
<button class="switch"> | |
<div class="switch__background"></div> | |
<svg class="egg" xmlns="http://www.w3.org/2000/svg"> | |
<defs> | |
</defs> | |
<g class="egg__mainGroup"> | |
<path class="egg__white -fried" d="M 67.254 7.183 C 67.254 7.183 42.919 -7.163 30.081 4.695 C 17.243 16.553 -16.134 54.902 9.313 84.817 C 19.285 93.772 29.336 96.076 43.999 96.349 C 73.363 99.935 75.215 91.591 94.133 61.592 C 113.051 31.593 67.254 7.183 67.254 7.183 Z" fill="#fefdfb"/> | |
<circle class="egg__yolk" cx="58.5" cy="43.5" r="25.5" fill="#facf37"/> | |
<ellipse class="egg__yolkShadow" cx="8" cy="13.5" rx="8" ry="13.5" transform="translate(72.557 39.322) rotate(60)" fill="#f7a721"/> | |
<path class="egg__yolkShadow" d="M690.365,785.646s-7.478,5.525-6.931,14.66" transform="translate(-643 -756)" fill="none" stroke="rgba(255,255,255,0.4)" stroke-linecap="round" stroke-width="3.5"/> | |
<path class="egg__yolkShadow" d="M701.481,782.382h0a21.465,21.465,0,0,0-6.366,1.112" transform="translate(-643 -756.381)" fill="none" stroke="rgba(255,255,255,0.4)" stroke-linecap="round" stroke-width="3.5" /> | |
<path class="egg__white -middle -hidden" d="M 50.812 17.156 C 50.812 17.156 41.587 16.271 30.728 20.391 C 19.869 24.511 7.378 33.636 0.846 53.657 C -5.251 79.707 22.691 86.701 50.812 86.701 C 84.851 86.701 101.263 75.568 98.15 53.58 C 89.481 16.599 50.812 17.156 50.812 17.156 Z" /> | |
<path class="egg__white -raw -hidden" d="M 40.95 0.03 C 35.525 -0.22 28.261 0.995 20.985 7.482 C 13.71 13.97 6.423 25.73 0.951 46.573 C -7.876 93.594 47.033 110.593 70.702 80.501 C 82.537 65.455 80.648 45.555 73.026 29.307 C 65.404 13.06 52.049 0.465 40.95 0.03 Z" /> | |
</g> | |
</svg> | |
</button> |
const switchButton = document.querySelector('.switch'); | |
const switchBackground = switchButton.querySelector('.switch__background'); | |
const egg = switchButton.querySelector('.egg'); | |
const eggMainGroup = egg.querySelector('.egg__mainGroup'); | |
const eggWhite = egg.querySelector('.egg__white.-fried'); | |
const eggWhiteMiddle = egg.querySelector('.egg__white.-middle'); | |
const eggWhiteRaw = egg.querySelector('.egg__white.-raw'); | |
const yolk = egg.querySelector('.egg__yolk'); | |
const yolkShadows = Array.from(egg.querySelectorAll('.egg__yolkShadow')); | |
const EGG_RAW_PATH = eggWhiteRaw.getAttribute('d'); | |
const EGG_MIDDLE_PATH = eggWhiteMiddle.getAttribute('d'); | |
const EGG_FRIED_PATH = eggWhite.getAttribute('d'); | |
const SWITCH_PADDING = Number( | |
getComputedStyle(eggMainGroup).transform.split(',')[4] | |
); | |
const { | |
switchBackgroundCoords, | |
eggCoords, | |
eggWhiteCoords, | |
eggWhiteRawCoords, | |
eggWhiteMiddleCoords, | |
yolkCoords, | |
} = (() => { | |
const elements = { switchBackground, egg, eggWhite, eggWhiteRaw, eggWhiteMiddle, yolk }; | |
return Object.keys(elements).reduce((previous, elementName) => { | |
return { ...previous, [`${elementName}Coords`]: elements[elementName].getBoundingClientRect() }; | |
}, {}); | |
})(); | |
let isAnimating = false; | |
const toggleAnimationController = () => { isAnimating = !isAnimating; }; | |
const toggleEggWhite = isOff => { | |
const scaleMiddle = 0.7; | |
const xMiddle = eggCoords.width / 2 - (eggWhiteCoords.width * scaleMiddle) / 2; | |
new TimelineLite() | |
.to(eggWhite, 0.25, { | |
x: xMiddle, | |
y: eggWhiteMiddleCoords.height / 4, | |
scale: scaleMiddle, | |
ease: Circ.easeIn, | |
attr: { | |
d: EGG_MIDDLE_PATH | |
} | |
}) | |
.to(eggWhite, 0.25, { | |
x: isOff ? '0%' : eggCoords.width - eggWhiteRawCoords.width - SWITCH_PADDING * 2, | |
y: '0%', | |
scale: 1, | |
ease: Back.easeOut.config(1.7), | |
attr: { | |
d: isOff ? EGG_FRIED_PATH : EGG_RAW_PATH | |
} | |
}); | |
}; | |
const toggleYolk = isOff => { | |
const xMiddle = eggCoords.width / 2 - yolkCoords.width; | |
const xRaw = | |
eggCoords.width - eggWhiteRawCoords.width - yolkCoords.width - SWITCH_PADDING / 2; | |
new TimelineLite() | |
.to(yolk, 0.25, { | |
x: xMiddle, | |
y: 10, | |
ease: Circ.easeIn, | |
opacity: 0 | |
}) | |
.to(yolk, 0.25, { | |
x: isOff ? '0%' : xRaw, | |
fill: isOff ? '#facf37' : '#FDDD71', | |
y: isOff ? '0%' : 15, | |
opacity: 1, | |
ease: Back.easeOut.config(1.7) | |
}); | |
}; | |
const toggleYolkShadows = isOff => { | |
const toggleShadow = item => { | |
new TweenLite(item, 0.1, { | |
...isOff ? { delay: 0.35 } : {}, | |
opacity: isOff ? 1 : 0, | |
}) | |
} | |
yolkShadows.forEach(toggleShadow); | |
}; | |
const toggleSwitchMovement = isOff => { | |
new TweenLite(switchButton, 0.4, { | |
x: isOff ? '0%' : '3vw', | |
ease: Power1.easeInOut | |
}); | |
}; | |
const toggleSwitchBackground = isOff => { | |
const x = isOff ? '0%' : eggCoords.width - switchBackgroundCoords.width; | |
new TweenMax(switchBackground, 0.5, { | |
x, | |
ease: Back.easeOut.config(1.7), | |
onComplete: toggleAnimationController, | |
}); | |
}; | |
const toggle = () => { | |
if (isAnimating) return; | |
toggleAnimationController(); | |
const isOff = switchButton.classList.contains('-off'); | |
switchButton.classList.toggle('-off'); | |
toggleSwitchBackground(isOff); | |
toggleEggWhite(isOff); | |
toggleYolk(isOff); | |
toggleYolkShadows(isOff); | |
toggleSwitchMovement(isOff); | |
}; | |
switchButton.addEventListener('click', toggle); |
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script> |
*, *::before, *::after { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
:root { | |
font-size: 62.5%; | |
} | |
html, body { | |
height: 100%; | |
} | |
body { | |
background: #f1f5fe; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
} | |
.switch { | |
background: transparent; | |
outline: none; | |
border: none; | |
cursor: pointer; | |
position: relative; | |
background: #3B3364; | |
border-radius: 100rem; | |
-webkit-tap-highlight-color: transparent; | |
} | |
.switch__background { | |
position: absolute; | |
top: 0; | |
left: 0; | |
background: #3B3364; | |
border-radius: 100rem; | |
width: 20rem; | |
height: 100%; | |
z-index: -1; | |
} | |
.egg { | |
height: 14.4rem; | |
width: 32.4rem; | |
} | |
.egg__mainGroup { | |
transform: translate(2.2rem, calc(2.2rem + 1.2px)); | |
} | |
.egg__white.-hidden { | |
visibility: hidden; | |
} |