乐趣区

关于前端:SVGIcon-组件的构建与使用

SVGIcon 是一个能够在我的项目任意中央应用的「图标组件」,组件使用者只需指定图标名称、色彩等属性,HTML 页面即可渲染出对应的 SVG 图标。本文将带大家理解 SVGlcon 组件的构建与应用。

背景

SVG 是什么?

SVG(Scalable Vector Graphics)可缩放矢量图形,是一种用于形容基于二维的矢量图形的 XML 标记语言,其根本矢量显示对象包含矩形、圆、椭圆、多边形、直线、任意曲线等,还能显示文字对象和嵌入式内部图像(包含 PNG、JPEG、SVG 等)。理论我的项目中大多数图标都是应用的 SVG 图标文件,其次要有以下几个 长处

  • 内容可读,文件是纯正的 XML。
  • 图像文件小,可伸缩性强。
  • 矢量放缩,能以不就义图像品质为前提,进行任意缩放。
  • 还能基于 DOM 模型实现动静和一些交互性能。

如何将 SVG 成果利用于 HTML 内容中?

在 HTML 文档写入相似于如下内容,则能在页面中渲染出对应的图标。

<div>
  <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
    <path d="M0 0h24v24H0V0z" fill="none"/>
    <path d="M18 20H4V6h9V4H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-9h-2v9zm-7.79-3.17l-1.96-2.36L5.5 18h11l-3.54-4.71zM20 4V1h-2v3h-3c.01.01 0 2 0 2h3v2.99c.01.01 2 0 2 0V6h3V4h-3z"/>
  </svg>
</div>

<icon1>
图标一

概括来讲,SVG 文件内容写入 HTML 文档中即可将 SVG 成果渲染展现到页面中。

如何在我的项目中优雅地应用大量的 SVG 图片文件?

前端实际我的项目时,为了谋求良好的用户体验,难免会应用到大量的合乎我的项目特色的图标,来丰盛丑化页面内容。所以随着我的项目逐步完善,须要应用到的图标文件必定也会随之减少,如何优雅地在我的项目中应用大量的 SVG 图片文件?这是咱们目前须要思考以及解决的重点问题之一。

理论我的项目开发中,采纳 <u> 间接将 SVG 文件内容(本质的 XML 内容)写入到 HTML 文档 </u> 对应地位去渲染咱们所预期的图标图形这种形式,可行但不可取!因为这种形式要求使用者在我的项目每个页面中每个须要展现 SVG 图标的地位,都要将图标文件内容残缺的写入对应的 DOM 中,而且 SVG 内容较繁冗,间接写入 DOM 十分影响咱们代码的好看和可浏览性。总之这样的操作太过轻便,<u> 重大不足灵活性和可扩展性 </u>。

通过技术调研,咱们发现 sprite-svg 联合 <abbr>use</abbr> 元素的应用形式,能够很好的解决这个问题。

  • 将我的项目中各个图标合并成一个蕴含多个 symbol 的 SVG 文件。
  • 在须要应用图标的中央,通过 <abbr>use</abbr> 元素援用对应的 symbol。

其工作原理是:<abbr>use</abbr> 元素从 SVG 文档内获得指标节点,并且复制它的内容。成果等同于指标节点被克隆到一个不可见的 DOM 中,而后将其粘贴到 <abbr>use</abbr> 元素的地位上。

Demo:

<!-- sprite.svg 文件目录:/dist/images/sprite.svg -->
<svg width="0" height="0" class="hidden">
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="add">
    <path fill="none" d="M0 0h24v24H0V0z"></path>
    <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="add_photo">
    <path d="M0 0h24v24H0V0z" fill="none"/>
    <path d="M18 20H4V6h9V4H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-9h-2v9zm-7.79-3.17l-1.96-2.36L5.5 18h11l-3.54-4.71zM20 4V1h-2v3h-3c.01.01 0 2 0 2h3v2.99c.01.01 2 0 2 0V6h3V4h-3z"/>
  </symbol>
</svg>
<div>
  <svg>
    <use xlinkHref={`/dist/images/sprite.svg#add_photo`} />
  </svg>
</div>

<icon2>
图标二

当然,如果 sprite-svg 曾经注入到了 HTML 文档中,<abbr>use</abbr> 元素的 xlinkHref 则可省去门路,间接应用 #id 去获取指标节点。

SVGIcon 图标组件的构建

有了以上常识储备,咱们能够大抵给出 SVGIcon 组件的构建方向:

  • 基于 <abbr>use</abbr> 元素,封装一个通用灵便的图标组件。
  • 聚合 SVG 图标,生成 sprite-svg 文件。

[工作一] 封装图标组件

组件 Props 的定义

首先须要定义好这个组件的 interface 输出,咱们所冀望的通用 SVG 组件,次要须要实现以下几点:

  • 指定图标 name、color 等属性,渲染对应的带有指定色彩的 SVG 图标。
  • 指定图标的 size,渲染对应大小的 SVG。

咱们将组件的 Props 输出定义如下,在 React.SVGProps 的根底上扩大 name、size、color 属性。

export interface Props extends React.SVGProps<SVGSVGElement> {
  name: string;
  size?: number;
  color?: iconColor;
}
组件外部实现思路

组件外部的实现,须要思考以下几点:

  • 如何依据 name 去 SVG 文档中获取指标节点。
  • 如何依据 size 指定 SVG 的大小。
  • 如何依据 color 去指定 SVG 的色彩。

解决思路:

  • 应用 <abbr>use</abbr> 元素去 sprite.svg 文档中依据 name 获取指标节点。
  • SVG 图标的 size 以及 color 等款式属性,能够应用 css 或者 style 管制。
难点:

难点一:如果使用者指定了组件的 size,同时又有通过 class 或者 style 去指定 SVG 的大小,组件外部到底以哪一个为准?咱们以 size 为准,如何实现呢?

  • style 行内款式的优先级比 class 指向的款式高(class 意旨内嵌样式表或者外嵌样式表中的某个款式)。
  • style 款式的规定之一是,在没有应用 !important 的前提条件下,有反复申明的款式时,以最初一次申明的为主。

所以咱们组件外部应用以下形式解决了这个问题:

// 伪代码
const size = 56;
const _style: React.CSSProperties = {
  ...style,
  width: `${size}px`,
  height: `${size}px`,
};

<svg style={_style}>
    <path fill="none" d="M0 0h24v24H0V0z"></path>
    <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path>
</svg>

通过以上的代码操作,SVG 则是一个依据 size = 56 失去的一个大小为 56px 的图标啦!

<icon3>
图标三

阐明:

对于 css 优先级的学习,参考:优先级 — css (层叠样式表) | MDN

难点二: 组件如何实现主动继承父级 color,并且做到兼容单色 icon 以及双色 icon 两种图标呢?

先给大家简略介绍一下 SVG 着色原理:

  • SVG 中 fill 能够间接应用 css 指定其色彩。
  • <abbr>SVG</abbr> 的 color 属性,能够为其 fill、stroke 属性提供一个潜在的间接值 currentColor,具体是什么意思呢?比方我指定 SVG 的 color=red,那么 SVG 中所有 path 上 fill=currentColor 的中央都会着色为 red。(参看下方 Demo)
  • 依据 fill='currentColor' 的特质,验证推断出 SVG 的 color 属性若为 currentColor,则能间接继承父级的色彩。

Demo:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <g color="red">
    <rect width="50" height="50" fill="currentColor" />
    <circle r="25" cx="70" cy="70" stroke="currentColor" fill="none" stroke-width="5" />
  </g>
</svg>

<icon4>
图标四

基于以上三点,咱们决定采纳双色 icon 向下兼容单色 icon 的解决形式来实现组件,大抵整顿出这个问题的解决思路如下:

1、图标组件默认为单色 icon,咱们能够给组件一个默认的 svg-icon 的 class 去指定 color 以及 fill 两个值都为 currentColor

.svg-icon {
  color: currentColor;
  fill: currentColor;
}

2、组件承受 color 参数,指定为双色 icon。同样的情理通过 css 款式去指定 SVG 的 color 和 fill 属性,因为是双色 icon,所以 color 和 fill 属性的色彩值须要有一个固定的差值,假如咱们拿到的 color=red,咱们则能够写如下的 css 实现主色为红色的双色 icon。

.svg-icon--red {color: var(--red-600);
  fill: var(--red-400);
}

须要留神的是,咱们须要限定 color 的取值范畴,反对无限集的 color 双色。这样则能够在 css 文件中,将所有的双色 icon 款式定制,组件侧引入后,外部就能够只依据 color 去增加对应的色彩款式实现双色了。

Demo:

// 伪代码 svg-icon 以及 svg-icon-${color}这些款式都须要提前在 css 文件中定义好
<svg
  className={classnames('svg-icon',  {[`svg-icon--${color}`]: color,
  })}
>
  <use xlinkHref={`/dist/images/sprite.svg#${name}`} />
</svg>

有了以上的构建思路,咱们就能够落实图标组件了!

源码

SVG 组件的实现源码:

type iconColor = 'red' | 'blue' | 'primary';  // 能够依据须要自行添加

function Icon(
  {
    name,
    size = 16,
    color,
    changeable,
    disabled,
    clickable,
    className,
    style,
    ...props
  }: Props,
  ref?: ForwardedRef<SVGSVGElement>,
): JSX.Element {
  const _style: React.CSSProperties = {
    ...style,
    width: `${size}px`,
    height: `${size}px`,
  };

  return (
    <svg
      {...props}
      ref={ref}
      data-name={name}
      style={_style}
      className={cs('svg-icon', className, {
        'svg-icon--changeable': changeable,
        'svg-icon--clickable': clickable,
        'svg-icon--disabled': disabled,
        [`svg-icon--${color}`]: color,
      })}
    >
      <use xlinkHref={`/dist/images/sprite.svg#${name}`} />
    </svg>
  );
}

SCSS 源码:

.svg-icon {
  @apply align-middle;
  display: inline-block;
  color: currentColor;
  fill: currentColor;
}

.svg-icon--blue {color: var(--blue-600);
  fill: var(--blue-400);
}

.svg-icon--red {color: var(--red-600);
  fill: var(--red-400);
}

.svg-icon--primary {color: var(--primary-600);
  fill: var(--primary-400);
}

.svg-icon--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.svg-icon--changeable {
  &:hover,
  &:active {color: var(--icon-coloured-color);
    fill: var(--icon-coloured-fill);
  }
}

.svg-icon--clickable {cursor: pointer;}

[工作二] 聚合 SVG 图标

目前,SVG Sprite 最佳实际是应用 symbol 元素。将各个图标合并成一个蕴含多个 symbol 的 SVG 文件,在须要应用图标的中央,援用对应的 symbol 即可。那么如何将多个图标合并成一个蕴含多个 symbol 的 SVG 文件呢?

  • 手动合并,这是最简略易上手的形式。我的项目初期应用大量 icon 的状况能够应用。然而当我的项目逐步宏大起来,一次性须要增加应用特地多 icon 的时候,手动合并显著不合时宜。
  • 利用插件合并,这无疑是最佳抉择,合并 SVG 的插件特地多,大家能够自行抉择喜爱的插件来进行合并。

全象云低代码平台应用的是 svg-spreact 插件来将 SVG 图标文件合并压缩,并进行转换为蕴含 symbol 的 SVG 文件,操作流程如下:

1、在每次我的项目构建的时候,执行 JavaScript 脚本,遍历寄存 SVG 的文件夹下所有 SVG 图标文件内容,输入取得 [{file: 文件名, cont:文件内容}] 这样的一维数组。
2、依据插件合并办法参数的须要,将遍历取得的数组信息进行解决并且赋值给对应的 svgSpreact 办法。

// 外围代码
const svgSpreact = require('svg-spreact');
const {defs} = await svgSpreact(input, { tidy: true, processId: iconID, svgoConfig})

参数阐明:

  • input:SVG 图片文件内容数组。
  • iconID:合并时的 SVG 的 id,倡议应用 SVG 图标文件的名称作为 id。
  • svgoConfig:压缩 SVG 内容的配置。
// svgoConfig 配置代码示例
module.exports = {
  multipass: false,
  plugins: [
    {
      name: 'removeAttributesBySelector',
      params: {
        selectors: [{ selector: "[fill ='none']", attributes: 'fill' },
          {selector: "[fill ='#94A3B8']", attributes: 'fill' },
        ],
      }
    },{
      name:  'removeTitle',
      active: true
    }
  ];

3、通过上一步操作执行 svgSpreact 办法后,失去的返回后果对象中的 defs 即为合并后蕴含多个 symbol 的 SVG 文件内容。最初将返回的后果内容输入写入到 /dist/images/sprite.svg 中:

const fs = require('fs');
const pathName = `/dist/images/sprite.svg`;
const spriteFile = path.join(basePath, pathName);
fs.writeFileSync(spriteFile, defs);

至此 SVG 图标既合并实现,合并后的 sprite-svg 文件的门路为 /dist/images/sprite.svg

留神:

理论我的项目中,大多都不能间接将聚合获取失去的 SVG 文件内容间接输入写入到 /dist/images/sprite.svg 中。在咱们的我的项目场景中,间接输入会导致所有的 icon 都保留原有的色彩,不能实现咱们预期须要的单色、双色图标。怎么解决这个问题呢?

单色、双色 icon 的原理以及实现思路有在本文「难点」局部解说,联合这个知识点,咱们能够对其进行以下解决:

首先确定 SVG 图标中哪一个 path 须要设置为主色,哪一个 path 须要设置为辅色。全象云前端是将 SVG 图标中的 #475569 作为主色,#94A3B8 作为辅色的。

// 示例 SVG 图标
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path fill-rule="evenodd" clip-rule="evenodd" d="M10 18.3333C7.68089 18.3333 5.55749 17.8478 4.16667 16.9278V11.1428C5.55749 12.0628 7.68089 12.5483 10 12.5483C12.3191 12.5483 14.4425 12.0628 15.8333 11.1428V16.9278C14.4425 17.8478 12.3191 18.3333 10 18.3333Z" fill="#94A3B8"/>
  <path fill-rule="evenodd" clip-rule="evenodd" d="M4.1667 3.57971C4.17712 4.62984 6.78479 5.48008 10 5.48008C13.2152 5.48008 15.8229 4.62984 15.8333 3.57971L15.8333 9.64309C14.4425 10.5631 12.3191 11.0486 10 11.0486C7.68089 11.0486 5.55749 10.5631 4.16667 9.64309V3.57971H4.1667Z" fill="#94A3B8"/>
  <ellipse cx="10" cy="3.57335" rx="5.83333" ry="1.90668" fill="#475569"/>
</svg>

<icon5>
图标五

聚合时,利用 svgoConfig 将内容中辅色值为 #94A3B8 的去掉,这样 fill 属性能应用 color 作为默认间接色彩值实现单色,能够独自制订色彩,实现双色。

module.exports = {
  multipass: false,
  plugins: [
    {
      name: 'removeAttributesBySelector',
      params: {
        selectors: [{ selector: "[fill ='none']", attributes: 'fill' },
          {selector: "[fill ='#94A3B8']", attributes: 'fill' },
        ],
      }
    },{
      name:  'removeTitle',
      active: true
    }
  ];

在 svgSpreact 聚合后拿到的 defs 内容,先将其中主色局部 #475569 字符串替换为 currentColor,而后再写入 /dist/images/sprite.svg

// 最终合并失去的 sprite.svg 文件
<svg width="0" height="0" class="hidden">
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" id="database">
    <path fill-rule="evenodd" d="M10 18.333c-2.32 0-4.443-.485-5.833-1.405v-5.785c1.39.92 3.514 1.405 5.833 1.405 2.32 0 4.443-.485 5.833-1.405v5.785c-1.39.92-3.514 1.405-5.833 1.405zM4.167 3.58c.01 1.05 2.618 1.9 5.833 1.9 3.215 0 5.823-.85 5.833-1.9v6.063c-1.39.92-3.514 1.406-5.833 1.406-2.32 0-4.443-.486-5.833-1.406V3.58z" clip-rule="evenodd"></path>
    <ellipse cx="10" cy="3.573" fill="currentColor" rx="5.833" ry="1.907"></ellipse>
  </symbol>
</svg>

图标组件的应用

实现了图标组件的封装,以及 sprite.svg 的聚合,接下来咱们就能够来灵便应用咱们的 icon 图标组件了。这里有个前提是,要应用到的图标必须都曾经聚合到了 sprite.svg 中,应用能力失效。

后期筹备:

在须要应用图标组件的 jsx 文件里,先将封装好的 icon 组件引入进来,后续能力应用。

import Icon from '/component/icon' // 组件门路视具体我的项目而定

应用形式

  • name:指定图标内容。
  • size:指定图标的大小。
  • color:指定图标为双色图标,并且色彩为 color 的值。若不传 color 则默认图标为单色图标,色彩默认从父级继承。
  • style:自定义图标组件的行内款式。
  • className:自定义图标款式。

示例 — 单色图标

<span style={{color: 'blue'}}>
  <Icon size={20} name='gateway' />
</span>

<icon6>
图标六

示例 — 双色图标

<div>
  <Icon color="blue" size={24} name='gateway' />
</div>

<icon7>
图标七

当然使用者也能够去应用 css 管制 SVG 组件图标的色彩。

Demo:

<div>
  <Icon
        name='gateway'
         size={24}
        style={{color: '#e11d48'; fill: '#8CADFF'}}
    />
</div>


图标八

总结

全象云低代码平台的图标组件的特点是,同时反对单色、双色 icon。使用方便,只需定义 color 属性,就能够管制图标的色彩模式是单色还是双色,以及管制色彩值。

公众号 :全象云低代码
官网:https://www.quanxiang.dev/
GitHub:https://github.com/quanxiang-…

退出移动版