需求
实现一个组件 展示一组按照规律伸缩的柱 用来描述正在传播的波
实现
Wave组件
"use client";
import { CSSProperties, FC, useLayoutEffect, useRef, useState } from "react";
import styles from "./styles.module.css";
import classnames from "classnames";
type WaveProps = {
/** 柱宽 */
width?: number;
/** 柱间隙 */
gap?: number;
/** 柱总数 默认撑满全部空间 */
total?: number;
/** 每一组包含的柱数量 */
group?: number;
/** 波的周期(毫秒) */
period?: number;
className?: string;
style?: CSSProperties;
};
export const Wave: FC<WaveProps> = (props) => {
const {
width = 5,
gap = 10,
total = 0,
group = 5,
period = 1000,
className,
style = {},
} = props;
const [num, setNum] = useState(total < 0 ? 0 : total);
const containerRef = useRef<HTMLDivElement | null>(null);
useLayoutEffect(() => {
const container = containerRef.current;
if (!container || total > 0) return;
const newNumber = Math.floor(container.clientWidth / (width + gap));
setNum(newNumber);
}, [gap, width, total]);
return (
<div
className={classnames(className, "flex items-center")}
style={{ ...style, gap }}
ref={containerRef}
>
{new Array(num).fill(null).map((_, index) => {
return (
<div
key={index}
className={styles.wave}
style={{
width,
animationDelay: `-${(index * period) / 2 / group}ms`,
animationDuration: `${period / 2}ms`,
}}
></div>
);
})}
</div>
);
};
styles.module.css
.wave {
background-color: black;
animation: wave 1.5s alternate infinite linear;
}
@keyframes wave {
from {
height: 20%;
}
to {
height: 100%;
}
}
效果
说明
- 每次进行动画只占用了半个周期 因此设置动画时长时需要除以2
- animationDelay要取负值 否则不会立刻进行动画