Skip to content

Instantly share code, notes, and snippets.

@Sheraff
Last active July 29, 2021 07:22
Show Gist options
  • Select an option

  • Save Sheraff/9bdcd37bf0e3e901acb357247697540a to your computer and use it in GitHub Desktop.

Select an option

Save Sheraff/9bdcd37bf0e3e901acb357247697540a to your computer and use it in GitHub Desktop.
jumpy dot radio group
{
"scripts": [],
"styles": []
}
<input id="1" type="radio" name="radios" value="1" />
<label for="1"><div class="dot"></div>Hello</label>
<input id="2" type="radio" name="radios" value="2" />
<label for="2"><div class="dot"></div>Bonjour</label>
<input id="3" type="radio" name="radios" value="3" />
<label for="3"><div class="dot"></div>Hola</label>
<input id="4" type="radio" name="radios" value="4" />
<label for="4"><div class="dot"></div>안녕하세요</label>
<input id="5" type="radio" name="radios" value="5" />
<label for="5"><div class="dot"></div>Guten Tag</label>
const radios = Array.from(document.querySelectorAll('label'))
let previous
radios.forEach(radio => radio.addEventListener('click', async ({ target }) => {
const current = target.closest('label')
if (previous && previous !== current) {
const y = current.offsetTop - previous.offsetTop
const distorsion = Math.abs(y)**.8
const duration = distorsion + 300
await new Promise(resolve => previous.firstElementChild.animate([
// keyframes
{ transform: 'none' },
{ transform: `translateY(${y / 2}px) translateX(${- distorsion / 10}px) scaleY(${1.2 + distorsion / 50}) scaleX(1.2)` }
], {
// timing options
duration,
easing: `cubic-bezier(${.2 + distorsion / 100},-0.4,.83,.67)`,
}).onfinish = resolve);
previous.classList.remove('checked')
current.classList.add('checked')
await new Promise(resolve => current.firstElementChild.animate([
// keyframes
{ transform: `translateY(${- y / 2}px) translateX(${- distorsion / 10}px) scaleY(${1.2 + distorsion / 50}) scaleX(1.2)` },
{ transform: 'none' }
], {
// timing options
duration,
easing: 'cubic-bezier(.57,1.55,.17,.33)',
}).onfinish = resolve);
}
if(!previous) {
current.classList.add('checked')
await Promise.all([
new Promise(resolve => current.firstElementChild.animate([
// keyframes
{ transform: `scaleY(1.7) scaleX(1.7)` },
{ transform: 'none' }
], {
// timing options
duration: 500,
easing: 'cubic-bezier(.57,1.55,.17,.33)',
}).onfinish = resolve),
new Promise(resolve => current.firstElementChild.animate([
// keyframes
{ opacity: 0 },
{ opacity: 1 }
], {
// timing options
duration: 300,
}).onfinish = resolve),
])
}
if(previous)
previous.classList.remove('checked')
previous = current
current.classList.add('checked')
}))
body {
background-color: #1f1f1f;
color: white;
}
[type=radio] {
display: none;
}
label.checked .dot {
background: currentColor;
}
label {
--margin: 1rem;
--dot: .7rem;
--radio: 1rem;
position: relative;
display: block;
margin: var(--margin);
padding-left: calc(var(--radio) + 1rem);
height: var(--radio);
line-height: var(--radio);
cursor: pointer;
}
label::after,
label .dot {
content: '';
display: block;
position: absolute;
border-radius: calc(var(--radio) / 2);
}
label::after {
left: 0;
top: 0;
height: var(--radio);
width: var(--radio);
box-shadow: 0 0 0 1px currentColor;
}
label .dot {
left: calc(var(--radio) / 2 - var(--dot) / 2);
top: calc(var(--radio) / 2 - var(--dot) / 2);
height: var(--dot);
width: var(--dot);
transform-origin: center center;
z-index: 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment