本文使用了 mui, Box 和 Stack 都相当于 div, 只是布局组件
import { Box, Stack } from '@mui/material';
import { useCallback } from 'react';
import { flexCenter } from '@utils/constant';
import style from './index.module.scss';
interface CheckboxProps {
// eslint-disable-next-line no-unused-vars
onChange: (value: boolean) => void;
checked: boolean;
content: [string, string];
}
function Checkbox(props: CheckboxProps) {
const { checked, content, onChange } = props;
const checkedChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const {
target: { checked },
} = event;
onChange(checked);
},
[onChange]
);
return (
<div className={style.checkbox__container}>
<input
type="checkbox"
id={style.checkbox_xyz}
hidden
defaultChecked={checked}
onChange={checkedChange}
/>
<label htmlFor={style.checkbox_xyz} className={style.knobs}></label>
<Stack className={style.layer} direction="row">
// male 和 female
<Box sx={{ ...flexCenter }}>{content[0]}</Box>
<Box sx={{ ...flexCenter }}>{content[1]}</Box>
</Stack>
</div>
);
}
Checkbox.defaultProps = {
checked: false,
};
export default Checkbox;
// 其实就是居中而已
export const flexCenter = {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
};
@use 'sass:math';
$height: 40px;
$width: 120px;
$p: 2px;
$py: 4px;
$px: 8px;
$blockBackground: #fff;
$layerBackground: #f4f2f2;
$borderRadius: 4px;
$colors: (#c0c4cc, #000);
.checkbox__container {
display: inline-block;
position: relative;
background-color: $layerBackground;
box-sizing: border-box;
width: $width;
height: $height;
padding: $p;
// checkbox 滑块
.knobs {
cursor: pointer;
z-index: 2;
position: absolute;
top: 50%;
left: 0;
transform: translate($p, -50%);
width: math.div($width - 2 * $p, 2);
background-color: $blockBackground;
height: $height - 2 * $p;
border-radius: $borderRadius;
transition: all 0.5s ease, left 0.5s cubic-bezier(0.5, 0, 0.5, 1);
}
// checkbox 底色
.layer {
position: relative;
height: $height - 2 * $p;
pointer-events: none;
z-index: 3;
& > div {
padding: $py $px;
box-sizing: border-box;
width: 50%;
transition: color 0.5s cubic-bezier(0.5, 0, 0.5, 1);
}
// 其实就是两个 div (male 和 female)的样式
@for $i from 1 through 2 {
& > div:nth-of-type(#{$i}) {
color: nth($colors, 3 - $i);
}
}
}
&,
& .layer {
border-radius: $borderRadius;
}
#checkbox_xyz {
// 防止手机端点击出现背景
-webkit-tap-highlight-color: transparent;
width: 100%;
height: 100%;
// 选中后移动滑块
&:checked ~ .knobs {
left: math.div($width - 2 * $p, 2);
}
&:active ~ .knobs {
transform-origin: center left;
// 点击后滑块变大一点点会感觉有弹性
transform: translate($p, -50%) scaleX(1.1);
}
&:checked:active ~ .knobs {
transform-origin: center right;
}
// 选中后改变字体颜色
&:checked ~ .layer {
@for $i from 1 through 2 {
& > div:nth-of-type(#{$i}) {
color: nth($colors, $i);
}
}
}
}
}