先上一个静态样式
再来一个gif
你们项目中应该也有类似的功能吧,觉得有用的话可以换上
功能点
- 激活的tab白底篮子(蓝字 哈哈哈)
- 当相邻的两个tab都是未激活时中间有一条分隔线
- 切换时白底移动,有一个动画
动画实现思路
使用transition: translateX 来移动白色背景
上代码
index.tsx
// 按钮样式的单选组
import classnames from "classnames";
import { useMemo } from "react";
import style from "./index.module.css";
type radioOption = {
value: string;
label: string;
};
type IRadioGroupOfButtonStyleProps = {
radioOptions: Array<radioOption>;
value: string;
handleRadio: any;
RadiosBoxStyle?: object;
radioStyle?: object;
radioActiveBgBoxStyle?: object;
};
const RadioGroupOfButtonStyle = (props: IRadioGroupOfButtonStyleProps) => {
const {
radioOptions,
value,
handleRadio,
RadiosBoxStyle,
radioStyle,
radioActiveBgBoxStyle,
} = props;
// 计算白底宽度
const radioWidth = useMemo(() => {
if (radioOptions.length) {
return Number((100 / radioOptions.length).toFixed(2));
} else {
return 100;
}
}, [radioOptions]);
const activeIndex: any = useMemo(() => {
let res = 0;
radioOptions.forEach((item, index) => {
if (item.value === value) {
res = index;
}
});
return res;
}, [value, radioOptions]);
return (
<div className={style.RadiosBox} style={RadiosBoxStyle}>
<div className={style.radioActiveBgBox} style={radioActiveBgBoxStyle}>
<div
className={style.radioActiveBg}
// 动画样式绑定
style={{
width: `${radioWidth}%`,
transform: `translateX(${activeIndex * 100}%)`,
}}
></div>
</div>
{radioOptions.map((options, index) => {
return (
<div
key={options.value}
className={style.radioBox}
onClick={() => handleRadio(options.value)}
>
<div
className={classnames({
[style.radio]: true,
[style.radioActive]: value === options.value,
})}
style={radioStyle}
>
{options.label}
</div>
<div
className={classnames({
[style.border]: true,
// 当相邻的两个tab都是未激活时中间有一条分隔线判断逻辑
[style.borderHidden]: !(
index !== radioOptions.length - 1 &&
index !== activeIndex &&
index !== activeIndex - 1
),
})}
></div>
</div>
);
})}
</div>
);
};
export default RadioGroupOfButtonStyle;
index.module.css
.RadiosBox {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #e6e8eb;
border-radius: 4px;
background-color: #f0f2f5;
cursor: pointer;
height: 32px;
box-sizing: border-box;
padding: 2px;
}
.radioBox {
position: relative;
min-width: 63px;
height: 28px;
border-radius: 2px;
line-height: 20px;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.radio {
position: relative;
box-sizing: border-box;
font-size: 14px;
color: #565c66;
padding: 6px 16px;
white-space: nowrap;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.radioActive {
color: #1370ff;
position: relative;
}
.border {
position: absolute;
right: 0px;
width: 1px;
height: 16px;
background-color: rgba(209, 214, 222, 1);
}
.borderHidden {
visibility: hidden;
}
.radioActiveBgBox {
width: calc(100% - 2px);
position: absolute;
left: 1px;
top: 1px;
height: 28px;
}
.radioActiveBg {
box-shadow: 2px 4px 12px 0px rgba(0, 27, 63, 0.06);
height: 100%;
background-color: #fff;
transition: transform 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
border-radius: 2px;
width: 50%;
transform: translateX(0px);
}
晚安~