最近发现css还挺好玩的、实现一个丝滑tab切换动效

646 阅读1分钟

先上一个静态样式

tab1.png

再来一个gif

20220913005242.gif

你们项目中应该也有类似的功能吧,觉得有用的话可以换上

功能点

  1. 激活的tab白底篮子(蓝字 哈哈哈)
  2. 当相邻的两个tab都是未激活时中间有一条分隔线
  3. 切换时白底移动,有一个动画

动画实现思路

使用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);
}

晚安~