<body>
<form>
<label>
<input type="radio" name="os" data-option="0" value="windows" checked>
Windows
</label>
<label>
<input type="radio" name="os" data-option="1" value="mac">
Mac
</label>
<label>
<input type="radio" name="os" data-option="2" value="linux">
Linux
</label>
</form>
</body>
{/tabs-pane}
{tabs-pane label="css"}
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=DM+Sans&display=swap">
<style>
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--hue: 223;
--bg: hsl(var(--hue),90%,80%);
--fg: hsl(var(--hue),90%,10%);
--radio-off: hsl(var(--hue),90%,90%);
--radio-on: hsl(var(--hue),90%,50%);
--radio-down: hsl(var(--hue),90%,70%);
--trans-dur: 0.3s;
--trans-timing: cubic-bezier(0.65,0,0.35,1);
font-size: calc(16px + (24 - 16) * (100vw - 320px) / (2560 - 320));
}
body,
input {
color: var(--fg);
font: 1em/1.5 "DM Sans", sans-serif;
transition:
background-color var(--trans-dur),
color var(--trans-dur);
}
body {
background-color: var(--bg);
display: flex;
height: 100vh;
}
form {
background-color: hsl(0,0%,100%);
border-radius: 0.75em;
margin: auto;
padding: 0.75em;
min-width: 15em;
transition: background-color var(--trans-dur);
}
label,
input[type="radio"] {
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
label {
display: flex;
align-items: center;
padding: 0.75em 0.75em 0.75em 3em;
position: relative;
}
input[type="radio"] {
background-color: var(--radio-off);
border-radius: 0.75em;
outline: transparent;
position: absolute;
bottom: 0.75em;
left: 0.75em;
width: 1.5em;
height: 1.5em;
transition: background-color var(--trans-dur) var(--trans-timing);
-webkit-appearance: none;
appearance: none;
}
input[type="radio"]:checked {
background-color: var(--radio-on);
}
input[type="radio"]:not(:checked):active {
background-color: var(--radio-down);
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(var(--hue),90%,10%);
--fg: hsl(var(--hue),90%,90%);
--radio-off: hsl(var(--hue),90%,40%);
--radio-on: hsl(var(--hue),90%,90%);
--radio-down: hsl(var(--hue),90%,60%);
}
form {
background-color: hsl(var(--hue),90%,20%);
}
}
</style>
{/tabs-pane}
{tabs-pane label="javascript"}
<script>
window.addEventListener("DOMContentLoaded",() => {
const c = new DroppingRadioButtons("form");
});
class DroppingRadioButtons {
optionID = 0;
optionAAnim = null;
optionBAnim = null;
constructor(el) {
this.el = document.querySelector(el);
this.el.addEventListener("change",this.updateOption.bind(this));
}
updateOption(e) {
const nextOption = +e.target.getAttribute("data-option");
if (this.optionID !== null) {
this.animateOption(this.optionID, nextOption);
}
this.optionID = nextOption;
}
animateOption(optionA,optionB) {
const diff = Math.abs(optionA - optionB);
const optionAEl = this.el.querySelector(`[data-option="${optionA}"]`);
const optionBEl = this.el.querySelector(`[data-option="${optionB}"]`);
const ALessThanB = optionA < optionB;
const dropColor = `var(--radio-${ALessThanB ? "on" : "off"})`;
const inchColor = `var(--radio-${ALessThanB ? "off" : "on"})`;
const dropKeyframes = [
{
backgroundColor: dropColor,
transform: `translateY(0)`,
zIndex: 1
},
{
backgroundColor: dropColor,
transform: `translateY(0)`,
zIndex: 1,
offset: 0.4,
easing: "cubic-bezier(0.32,0,0.67,0)"
},
{
backgroundColor: dropColor,
transform: `translateY(${3 * diff}em)`,
zIndex: 1,
offset: 0.5,
easing: "cubic-bezier(0.32,1,0.67,1)"
},
{
backgroundColor: dropColor,
transform: `translateY(${2.5 * diff}em)`,
zIndex: 1,
offset: 0.6,
easing: "cubic-bezier(0.32,0,0.67,0)"
},
{
backgroundColor: dropColor,
transform: `translateY(${3 * diff}em)`,
zIndex: 1,
offset: 0.7
},
{
backgroundColor: dropColor,
transform: `translateY(${3 * diff}em)`,
zIndex: 1
}
];
const inchUpKeyframes = [
{
backgroundColor: inchColor,
height: `1.5em`,
bottom: `0.75em`
},
{
backgroundColor: inchColor,
height: `${1.5 + 3 * diff}em`,
bottom: `0.75em`,
offset: 0.1
},
{
backgroundColor: inchColor,
height: `${1.5 + 3 * diff}em`,
bottom: `0.75em`,
offset: 0.9
},
{
backgroundColor: inchColor,
height: `1.5em`,
bottom: `${0.75 + 3 * diff}em`
}
];
const duration = 900;
const bezier = "cubic-bezier(0.65,0,0.35,1)";
const optionAConfig = { duration };
const optionBConfig = { duration };
if (ALessThanB) {
optionBConfig.easing = bezier;
} else {
optionAConfig.easing = bezier;
}
this.optionAAnim?.cancel();
this.optionAAnim = optionAEl.animate(
ALessThanB ? dropKeyframes : inchUpKeyframes,
optionAConfig
);
this.optionBAnim?.cancel();
this.optionBAnim = optionBEl.animate(
ALessThanB ? inchUpKeyframes : dropKeyframes,
optionBConfig
);
}
}
</script>
{/tabs-pane}
代码来自codepenDropping Radio Buttons
评语