Icon组件

Icon组件,比较常用的做法就是通过一个create icon的方法,把svg转换成react组件,让svg的内容直接贴在html中。然后我们还希望能够控制svg的大小、颜色、是否旋转。

  • Color: 颜色的控制比较简单,就是通过fill="currentColor",然后外部的父元素的字体颜色,就会直接继承给svg。

  • Size: 大小就要通过传size属性值去控制,注意size可以传字符串(字体大小)也可以传字符串数组(宽高)。

  • Sprin: 这个通过写一个公共的旋转样式,当为true的时候就用上这个样式就好了。

icon.tsx

import { forwardRef, PropsWithChildren } from "react";
import cs from 'classnames';
import './index.scss';

type BaseIconProps = {
  className?: string;
  style?: React.CSSProperties;
  size?: string | string[];
  spin?: boolean;
}

export type IconProps = PropsWithChildren<BaseIconProps & Omit<React.SVGAttributes<SVGElement>, keyof BaseIconProps>>;

// ['10px', '20px'] or '10px'
// 如果传了['10px'] 则等宽高10px
export const getSize = (size: IconProps['size']) => {
  if (Array.isArray(size) && size.length === 2) {
    return size;
  }
  const width = (size as string) || '1em';
  const height = (size as string) || '1em';
  return [width, height];
}

export const Icon = forwardRef<SVGSVGElement, IconProps>(
  (props, ref) => {
    const {
      style,
      className,
      children,
      size,
      spin,
      ...restProps
    } = props;

    const [width, height] = getSize(size);
    const cn = cs('icon', { 'icon-spin': spin }, className);

    return (
      <svg ref={ref} className={cn} style={style} width={width} height={height} fill="currentColor" {...restProps}>
        {children}
      </svg>
    )
  }
)

icon.scss

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

.icon {
  display: inline-block;
}

.icon-spin {
  animation: spin 1s linear infinite;
}

为了方便使用,再写一个创建icon的工厂函数, 这里在参数上的设计,有一点想法。

createIcon:定义svg元素的固有属性,所以把viewBox放在这一层。

icon:定义svg元素的个性化属性

import React, { forwardRef } from "react";
import { IconProps, Icon } from "./Icon";

export interface CreateIconOptions {
  content: React.ReactNode;
  iconProps?: IconProps;
  viewBox?: string;
}

export const createIcon = (options: CreateIconOptions) => {
  const { content, iconProps = {}, viewBox='0, 0, 1024, 1024' } = options;

  return forwardRef<SVGSVGElement, IconProps>((props, ref) => {
    return <Icon ref={ref} viewBox={viewBox} {...iconProps} {...props} >
            {content}
          </Icon>;
  })
}

然后就可以去创建我们的icon组件了。

import { createIcon } from '../createIcon';

export const IconAdd = createIcon({
  content: (
    <>
      <path d="M853.333333 480H544V170.666667c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v309.333333H170.666667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h309.333333V853.333333c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V544H853.333333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z"></path>
    </>
  ),
});
posted @ 2026-01-30 17:38  爽爽susan  阅读(1)  评论(0)    收藏  举报