Turn airplane mode on and off! inspired by a Dribbble shot: https://dribbble.com/shots/9748924-Airplane-Mode-Toggle
A Pen by Kiarash Zarinmehr on CodePen.
Turn airplane mode on and off! inspired by a Dribbble shot: https://dribbble.com/shots/9748924-Airplane-Mode-Toggle
A Pen by Kiarash Zarinmehr on CodePen.
<div class="switch"> | |
<div class="switch__button"> | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
class="airplane" | |
viewBox="0 0 140.351 139.63" | |
> | |
<path fill="var(--color-off)" d="M45.851,139.316s13.2-12.046,11.842-49.753c-41.162-3.584-41.285-3.448-41.285-3.448L10.1,97.9H0L7.725,69.815h0L0,41.729H10.1l6.308,11.785s.123.136,41.285-3.449C59.047,12.359,45.851.314,45.851.314s15.674-.706,17.187,0c.364.169,2.2,3.17,4.789,7.635H82.858a6,6,0,0,1,0,12H74.635c1.651,2.949,3.352,6.007,5.009,9h21.213a6,6,0,1,1,0,12h-14.6C89.473,46.806,91.7,50.9,91.7,50.9s15.527.144,31.431,1.838S140.347,69.48,140.347,69.48v.32c0,.01,0,.02,0,.031l0,.206s-1.31,15.163-17.215,16.856S91.7,88.73,91.7,88.73s-2.228,4.093-5.442,9.95h14.6a6,6,0,1,1,0,12H79.645c-1.658,2.993-3.358,6.051-5.009,9h8.222a6,6,0,1,1,0,12H67.828c-2.587,4.465-4.426,7.465-4.79,7.636a22.453,22.453,0,0,1-5.128.313C52.817,139.63,45.851,139.316,45.851,139.316Z" | |
/> | |
</svg> | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
class="airplane -on" | |
viewBox="0 0 140.351 139.63" | |
> | |
<path fill="var(--color-on)" d="M45.851,139.316s13.2-12.046,11.842-49.753c-41.162-3.584-41.285-3.448-41.285-3.448L10.1,97.9H0L7.725,69.815h0L0,41.729H10.1l6.308,11.785s.123.136,41.285-3.449C59.047,12.359,45.851.314,45.851.314s15.674-.706,17.187,0c.364.169,2.2,3.17,4.789,7.635H82.858a6,6,0,0,1,0,12H74.635c1.651,2.949,3.352,6.007,5.009,9h21.213a6,6,0,1,1,0,12h-14.6C89.473,46.806,91.7,50.9,91.7,50.9s15.527.144,31.431,1.838S140.347,69.48,140.347,69.48v.32c0,.01,0,.02,0,.031l0,.206s-1.31,15.163-17.215,16.856S91.7,88.73,91.7,88.73s-2.228,4.093-5.442,9.95h14.6a6,6,0,1,1,0,12H79.645c-1.658,2.993-3.358,6.051-5.009,9h8.222a6,6,0,1,1,0,12H67.828c-2.587,4.465-4.426,7.465-4.79,7.636a22.453,22.453,0,0,1-5.128.313C52.817,139.63,45.851,139.316,45.851,139.316Z" | |
/> | |
</svg> | |
</div> | |
<div class="airport-wrapper"> | |
<div class="airport"> | |
<div class="airport__lightLine"> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
</div> | |
<div class="airport__airstrip"> | |
<div class="airport__lineContainer"> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
<div class="airport__line"></div> | |
</div> | |
</div> | |
<div class="airport__lightLine"> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
<div class="airport__light"></div> | |
</div> | |
</div> | |
</div> | |
<div class="sky"> | |
<div class="cloud-line"> | |
<div class="cloud-container"> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
</div> | |
<div class="cloud-container"> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
<div class="cloud"> | |
<div class="cloud__shape"></div> | |
<div class="cloud__shape"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> |
const switchContainer = document.querySelector('.switch'); | |
const airplanes = switchContainer.querySelectorAll('.airplane'); | |
const switchButton = switchContainer.querySelector('.switch__button'); | |
const cloudLine = document.querySelector('.cloud-line'); | |
const sky = document.querySelector('.sky'); | |
const airport = document.querySelector('.airport'); | |
const remSize = parseFloat(getComputedStyle(document.documentElement).fontSize); | |
const getStyleVariable = (property, element = document.documentElement) => { | |
return getComputedStyle(element).getPropertyValue(`--${property}`); | |
}; | |
const animateClouds = (() => { | |
const cloudFinalX = -cloudLine.children[0].clientWidth; | |
const cloudTimeline = gsap.timeline({ repeat: -1 }); | |
cloudTimeline.to(cloudLine, 4, { x: cloudFinalX, duration: 2, ease: 'none' }); | |
})(); | |
let isAnimating = false; | |
const switchButtonFinalX = | |
switchContainer.clientWidth - switchButton.clientWidth; | |
const airplanesFinalX = | |
airplanes[0].clientWidth + | |
switchButton.clientWidth / 2 - | |
airplanes[0].clientWidth / 2; | |
const airplanesInitialX = | |
-1 * (switchButton.clientWidth / 2 - airplanes[0].clientWidth / 2); | |
const toggleAnimation = isOn => { | |
const ease = 'power2.inOut'; | |
const duration = 0.5; | |
const nextState = isOn ? 'off' : 'on'; | |
const background = getStyleVariable(`color-background-${nextState}`); | |
const boxShadow = getStyleVariable(`shadow-${nextState}`); | |
const switchButtonX = isOn ? 0 : switchButtonFinalX; | |
const airplanesX = isOn ? airplanesInitialX : airplanesFinalX; | |
const airportX = isOn ? -110 : -20; | |
const opacity = isOn ? 0 : 1; | |
gsap.to(document.body, duration, { background, ease }); | |
gsap.to(switchContainer, duration, { boxShadow, ease }); | |
gsap.to(switchButton, duration, { | |
x: switchButtonX, | |
ease | |
}); | |
gsap.to(airplanes, duration, { | |
x: airplanesX, | |
ease | |
}); | |
gsap.to(airport, duration, { | |
x: airportX, | |
ease | |
}); | |
gsap.to(sky, duration, { | |
opacity, | |
ease, | |
onComplete() { | |
isAnimating = false; | |
} | |
}); | |
}; | |
const configOn = { | |
background: getStyleVariable('color-background-on'), | |
shadow: getStyleVariable('shadow-on'), | |
opacity: 1, | |
switchButtonX: switchButtonFinalX, | |
airplanesX: airplanesFinalX | |
}; | |
const toggleSwitch = ({ currentTarget }) => { | |
if (isAnimating) return; | |
isAnimating = true; | |
const isCurrentlyOn = currentTarget.classList.contains('-on'); | |
currentTarget.classList.toggle('-on'); | |
toggleAnimation(isCurrentlyOn); | |
}; | |
switchContainer.addEventListener('click', toggleSwitch); |
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.0.5/gsap.min.js"></script> |
*, *::after, *::before { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
:root { | |
--color-background-off: #4e4e4e; | |
--color-off: #494949; | |
--shadow-off: 0 1.2rem 1.5rem #4d4d4d; | |
--color-background-on: #2f86d5; | |
--color-on: #1AB1FD; | |
--shadow-on: 0 1.2rem 1.5rem #2c82cc; | |
font-size: 62.5%; | |
} | |
html, body { | |
height: 100%; | |
} | |
body { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
background: var(--color-background-off); | |
overflow: hidden; | |
} | |
.switch { | |
--switch-width: 31.1rem; | |
--switch-height: 13.8rem; | |
width: var(--switch-width); | |
height: var(--switch-height); | |
border-radius: 10rem; | |
box-shadow: var(--shadow-off); | |
position: relative; | |
cursor: pointer; | |
-webkit-tap-highlight-color: transparent; | |
} | |
.switch__button { | |
width: var(--switch-height); | |
height: var(--switch-height); | |
border-radius: 50%; | |
position: absolute; | |
left: 0; | |
top: 0; | |
z-index: 1; | |
overflow: hidden; | |
background: #fff; | |
z-index: 3; | |
} | |
.airplane { | |
position: absolute; | |
top: 50%; | |
width: calc(var(--switch-height) / 2); | |
height: calc(var(--switch-height) / 2); | |
} | |
.airplane:not(.-on) { | |
left: 50%; | |
transform: translate(-50%, -50%); | |
} | |
.airplane.-on { | |
transform: translateY(-50%); | |
right: 100%; | |
} | |
/* Airport Background */ | |
.airport-wrapper { | |
position: absolute; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
border-radius: inherit; | |
background: #848484; | |
overflow: hidden; | |
} | |
.airport-wrapper::before, .airport-wrapper::after { | |
content: ""; | |
position: absolute; | |
width: 100%; | |
height: 0.5rem; | |
background: #bebebe; | |
z-index: 1; | |
} | |
.airport-wrapper::before { | |
top: 18%; | |
} | |
.airport-wrapper::after { | |
bottom: 18%; | |
} | |
.airport { | |
--light-size: 0.6rem; | |
--line-size: 1.5rem; | |
width: 100%; | |
height: 100%; | |
border-radius: inherit; | |
display: flex; | |
flex-direction: column; | |
background: #848484; | |
} | |
.airport__lightLine { | |
height: 18%; | |
display: flex; | |
align-items: center; | |
padding-left: 3.1rem; | |
width: 200%; | |
} | |
.airport__light { | |
width: var(--light-size); | |
height: var(--light-size); | |
border-radius: 50%; | |
background: #f7bd00; | |
} | |
.airport__light:not(:last-child) { | |
margin-right: 3rem; | |
} | |
.airport__airstripWrapper { | |
border-top: 0.5rem solid #bebebe; | |
border-bottom: 0.5rem solid #bebebe; | |
} | |
.airport__airstrip { | |
height: 64%; | |
display: flex; | |
} | |
.airport__lineContainer { | |
display: flex; | |
align-items: center; | |
height: 100%; | |
margin-left: -0.4rem; | |
} | |
.airport__line { | |
width: var(--line-size); | |
height: 0.5rem; | |
background: #fff; | |
} | |
.airport__line:not(:last-child) { | |
margin-right: 2.1rem; | |
} | |
/* Sky Background */ | |
.sky { | |
width: 100%; | |
height: 100%; | |
position: absolute; | |
left: 0; | |
top: 0; | |
border-radius: inherit; | |
background: #78c7fe; | |
opacity: 0; | |
z-index: 2; | |
overflow: hidden; | |
} | |
.cloud-line { | |
width: 100%; | |
height: 100%; | |
position: absolute; | |
left: 0; | |
top: 0; | |
} | |
.cloud-container { | |
position: absolute; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
} | |
.cloud-container:last-child { | |
left: 100%; | |
} | |
.cloud { | |
width: 3rem; | |
height: 1rem; | |
position: absolute; | |
background: #fff; | |
} | |
.cloud::before, .cloud::after, .cloud__shape { | |
background: inherit; | |
border-radius: 50%; | |
position: absolute; | |
} | |
.cloud::before { | |
content: ""; | |
left: -0.5rem; | |
bottom: 0; | |
width: 1.5rem; | |
height: 1.5rem; | |
} | |
.cloud::after { | |
content: ""; | |
right: -0.5rem; | |
bottom: 0; | |
width: 1.3rem; | |
height: 1.3rem; | |
} | |
.cloud__shape:first-child { | |
left: 0; | |
bottom: 0.6rem; | |
width: 1.7rem; | |
height: 1.7rem; | |
} | |
.cloud__shape:last-child { | |
right: 0.2rem; | |
bottom: 0.5rem; | |
width: 1.4rem; | |
height: 1.4rem; | |
} | |
.cloud:first-child { | |
left: -0.1rem; | |
top: 3.2rem; | |
opacity: 0.6; | |
} | |
.cloud:nth-child(2) { | |
left: 5rem; | |
transform: scale(1.2); | |
bottom: 2.4rem; | |
opacity: 0.65; | |
} | |
.cloud:nth-child(3) { | |
left: 11rem; | |
transform: scale(1.2) scaleX(-1); | |
top: 6.4rem; | |
opacity: 0.5; | |
} | |
.cloud:nth-child(4) { | |
right: 7.6rem; | |
transform: scale(1.4) scaleX(-1); | |
top: 2.9rem; | |
opacity: 0.4; | |
} | |
.cloud:nth-child(5) { | |
right: 3.5rem; | |
bottom: 2rem; | |
opacity: 0.5; | |
} |