您可以参数化 svg,以便通过全局抽象轻松自定义 JSX.Element 级别。如何?使用 React 的 FC。尝试执行以下操作:
interface SvgIconConstituentValues {
strokeColor?: string;
strokeWidth?: string;
strokeWidth2?: string;
strokeWidth3?: string;
strokeFill?: string;
fillColor?: string;
fillColor2?: string;
fillColor3?: string;
fillColor4?: string;
fillColor5?: string;
fillColor6?: string;
fillColor7?: string;
imageWidth?: string;
imageHeight?: string;
width?: string;
height?: string;
rotateCenter?: number;
className?: string;
className2?: string;
className3?: string;
className4?: string;
className5?: string;
}
export default SvgIconConstituentValues;
- 将
SvgIconConstituentValues 导入到 tsx 文件中
- 将
{ FC } 从 React 导入到同一个 tsx 文件中
import { FC } from 'react';
import SvgIconConstituentValues from 'types/svg-icons';
// FC can be parameterized via Abstraction
- 创建一个扩展
FC 和SvgIconConstituentValues 的SvgIcon 接口
export interface SvgIcon extends FC<SvgIconConstituentValues> {}
- 使用
SvgIcon 参数化抽象svg 的属性,如下所示
export const ArIcon: SvgIcon = ({
width = '8.0556vw',
height = '8.0556vw',
strokeColor = `stroke-current`,
strokeWidth = '2',
fillColor = 'none',
fillColor2 = `fill-primary`,
rotateCenter = 0,
className = ` antialiased w-svgIcon max-w-svgIcon`,
className2 = ` stroke-current`,
className3 = ` fill-primary`
}): JSX.Element => {
return (
<svg
width={width}
height={height}
viewBox='0 0 65 65'
fill={fillColor}
xmlns='http://www.w3.org/2000/svg'
className={className}
transform={`rotate(${rotateCenter}, 65, 65)`}
>
<circle
cx='32.5'
cy='32.5'
r='31.5'
stroke={strokeColor}
strokeWidth={strokeWidth}
className={className2}
/>
<path
d='M30.116 39H32.816L27.956 26.238H25.076L20.18 39H22.808L23.87 36.084H29.054L30.116 39ZM26.462 28.992L28.226 33.816H24.698L26.462 28.992ZM40.7482 39H43.5202L40.7842 33.78C42.4582 33.294 43.5022 31.944 43.5022 30.162C43.5022 27.948 41.9182 26.238 39.4342 26.238H34.4482V39H36.9502V34.086H38.2462L40.7482 39ZM36.9502 31.944V28.398H38.9662C40.2262 28.398 40.9642 29.1 40.9642 30.18C40.9642 31.224 40.2262 31.944 38.9662 31.944H36.9502Z'
fill={fillColor2}
className={className3}
/>
</svg>
);
};
-
如您所见,抽象了三个独立的className参数(1、2、3):(1)className for <svg>...</svg>,property JSX.IntrinsicElements.svg: SVGProps<SVGSVGElement>; (2)<circle />属性JSX.IntrinsicElements.circle: SVGProps<SVGCircleElement>的className2; (3) <path /> 属性 JSX.IntrinsicElements.path: SVGProps 的 className3。
-
请注意,const ArIcon: SvgIcon = ({ ... }): JSX.Element => {...} 确实是一个 JSX.Element。因此,<svg></svg> 本身和任何子节点(圆、路径等)都是JSX.IntrinsicElements,每个都允许有自己唯一的className。这些 className 调用是手动添加到 svg 中的,transform 调用也是如此(将内联图标旋转到别处)。
-
JSX.IntrinsicElements 的JSX Attribute className 定义如下
SVGAttributes<T>.className?: string | undefined
-
每个 JSX.IntrinsicElement 都有权使用 className 属性。一个 svg 中有 100 条路径和一个圆圈?您可以拥有 102 个可以通过抽象参数化的类名。
-
现在是最好的部分。以下来自我的投资组合中的一个文件,我对抽象 svg 参数进行了修改,以使其与暗模式切换 (use-dark-mode) 和屏幕宽度相关的图标渲染 (@artsy/fresnel) 配合得很好。您可以全局导入此图标并在每个 JSX.Element 内内联调用参数,而无需传递任何道具
import { ArIcon } from 'components/svg-icons';
import Link from 'next/link';
import { Media } from 'components/window-width';
import { Fragment } from 'react';
import DarkMode from 'components/lead-dark-mode';
const ArIconConditional = (): JSX.Element => {
const arIconXs: JSX.Element = (
<Media at='xs'>
<Link href='/'>
<a
className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full '
id='top'
aria-label='top'
>
<ArIcon width='18vw' height='18vw' className='transition-all transform translate-y-90'
className2='transition-all duration-1000 delay-200 transform' className3='text-secondary fill-secondary' />
</a>
</Link>
</Media>
);
const arIconSm: JSX.Element = (
<Media at='sm'>
<Link href='/'>
<a
className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full '
id='top'
aria-label='top'
>
<ArIcon width='15vw' height='15vw' className='' className2='' className3='' />
</a>
</Link>
</Media>
);
const arIconMd: JSX.Element = (
<Media at='md'>
<Link href='/'>
<a
className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full '
id='top'
aria-label='top'
>
<ArIcon width='12.5vw' height='12.5vw' className='' className2='' className3='' />
</a>
</Link>
</Media>
);
const arIconDesktop: JSX.Element = (
<Media greaterThan='md'>
<Link href='/'>
<a
className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full '
id='top'
aria-label='top'
>
<ArIcon width='10vw' height='10vw' className='' className2='' className3='' />
</a>
</Link>
</Media>
);
const ArIconsCoalesced = (): JSX.Element => (
<Fragment>
<div className='relative block justify-between lg:w-auto lg:static lg:block lg:justify-start transition-all w-full min-w-full col-span-5'>
{arIconXs}
{arIconSm}
{arIconMd}
{arIconDesktop}
</div>
</Fragment>
);
return (
<Fragment>
<div className='select-none relative z-1 justify-between pt-portfolioDivider navbar-expand-lg grid grid-cols-6 min-w-full w-full container overflow-y-hidden overflow-x-hidden transform'>
<ArIconsCoalesced />
<div className='pt-portfolio'>
<DarkMode />
</div>
</div>
</Fragment>
);
};
export default ArIconConditional;
- 这个project 使用tailwindcss 和React 的Next.js 框架。也就是说,如果我希望包含图标的
JSX.IntrinsicElement 圆圈仅在移动设备上脉冲呢?添加顺风的animate-pulse到className2如下
// ...
const arIconXs: JSX.Element = (
<Media at='xs'>
<Link href='/'>
<a
className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full '
id='top'
aria-label='top'
>
<ArIcon width='18vw' height='18vw' className='transition-all transform translate-y-90'
className2='transition-all duration-1000 delay-200 transform animate-pulse' className3='text-secondary fill-secondary' />
</a>
</Link>
</Media>
);
// ...
-
fill-primary 调用是为 .dark-mode 和 .light-mode css 类定义的 css 变量,然后将其传递给 :root 并在客户端 (onChange={darkMode.toggle}) 切换暗模式时激活。
-
因此,onClick={darkMode.enable} 触发图标以更改其 fillColor 和 strokeColor 值作为 css 变量的函数。使用 React 的 FC 通过抽象来参数化 props 会产生真正卓越的粒度控制。在全局范围内使用 JSX.Element 级别的内联调用自定义 SVG 从未如此无缝。
-
darkMode.disable
- 查看我的recent DEV post,如果使用 typescript 破解 react-fontawesome 库,然后创建自定义 fontawesome SVG 图标,这些图标会持续到生产环境并且不会因库版本更新而改变,这激起了您的兴趣。
干杯