实现描述"录音"情景的动画效果

77 阅读1分钟

需求

实现一个组件 展示一组按照规律伸缩的柱 用来描述正在传播的波

实现

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%;
  }
}

效果

动画.gif

说明

  • 每次进行动画只占用了半个周期 因此设置动画时长时需要除以2
  • animationDelay要取负值 否则不会立刻进行动画