How does react set the svg icon library with customizable colors (2)

Processing of svg color part

Our expected usage is something like this:

<Icon name="account" color="red" size={
    
    [50,50]}/>
或者 size={
    
    50} 这样的写法

So three parameters are required:

  • name
  • color
  • size

After the steps in the previous article, you can see that the requirement of name has been realized.

The implementation of size is relatively simple, so I won’t go into too much detail (the process of processing size is also reflected in the following code), just add attributes to svg directly. Let’s take a simple example:

return (
    <svg
      width={
    
    200}  // 把这里换成变量就可以了,或者将样式统一放在一个对象里,然后用展开符也行
      height={
    
    200}
      aria-hidden="true"
    >
      <use xlinkHref={
    
    iconName} />
    </svg>
  )

Regarding the processing of colors, there are two different requirements:
one is to directly remove the color that comes with the original svg, and all colors are reset by the user (or have a default color);
the other is only when the user has passed the attribute of color Only use the custom color when using, otherwise keep the original svg color; (this is also my requirement)

For these two different requirements, the processing scheme is different (only when svg-sprite-loader is used). The first requirement is relatively simple to implement, and the second is more troublesome, which will be discussed separately below (I have no practice in some cases, so I only give ideas or references).

(1) Remove the original svg color

There are several ideas:
1. Directly remove the color value of fill in the original svg image;
2. Set the currentColor through css to overwrite the original color;

Directly remove the color value of the original fill

You can use some plug-ins (such as svgo-loader) or write some scripts yourself.

Install svgo-loader

GitHub:svgo-loader

SVGO converts SVG-as-XML data into an SVG-as-JS AST representation. It then runs on all AST data items and performs some operations, and finally, SVGO converts the AST back into an SVG-as-XML data string.
SVGO is an svg optimizer with many plugins. It can remove and modify SVG elements, fold content, move attributes, and more.
-------- Excerpted from "Using svg-sprite-loader to optimize Icon"

The main thing is webpack.config.jsto add automatic elimination in the configuration file fill, the specific code is as follows (from the Nuggets author moonwanger ):

{
    
    
     test: /\.svg$/,
     use: [
      {
    
     loader: 'svg-sprite-loader', options: {
    
    } },
      {
    
     loader: 'svgo-loader', options: {
    
    
         plugins:[
         // 加载时删除svg默认fill填充色
          {
    
    removeAttrs:{
    
    attrs: 'fill'}}
         ]
      }}
     ]
    },

Set currentColor via css

Just create a new style file (such as icon.less) and write the following css; then import this style file into the previously written general component icon.js. In icon.js, colorjust add the attribute to the svg tag.

g[fill] {
    
    
    fill: currentColor;
    fill-opacity: 1;
}
g[stroke] {
    
    
    stroke: currentColor;
    stroke-opacity: 1;
}
path[fill] {
    
    
    fill: currentColor;
    fill-opacity: 1;
}
path[stroke] {
    
    
    stroke: currentColor;
    stroke-opacity: 1;
}

The principle is very simple. In essence, it is to change the attribute of gthe tag or tag in the svg file to control the color.path

(2) Keep the original svg color

There are several ideas:
1. Dynamically introduce different css files, similar to the implementation of switching different themes of the website; (but I have not succeeded in this one)
2. Use the js selector to select the corresponding tag according to the id, when the user passes in the color Add style to the label when attribute;

The second method is the one I finally adopted after trying several methods and failed. The idea is very simple: by analyzing the HTML page after using svg-sprite-loader, it can be found that the symbol id corresponding to each svg image is different. ; The child of the symbol tag is the path of the svg image, and the fillattribute controls the color of the tag. In this way, we can use to document.getElementById()select this symboltag; then use childrento get its child elements, and then control fillthe attribute in the path. In this way, when the user does not pass in the color,

HTML after using svg-sprite-loader

The directory structure in my project is like this:

- icons
	- svg 这个文件夹用来放所有的svg图片
- src
	- app.js 

Icons.js:

import React, {
    
     useMemo, useState, useEffect } from 'react'

const Icon = ({
     
     name,size,color}) => {
    
    
    console.log(name,size,color)

    const [svgModule, setSvgModule] = useState();
    const [svgSize, setSvgSize] = useState({
    
    
      width: 30,
      height:30
    });

  // 允许自定义颜色
  const setColor = () => {
    
    
    let elem = document.getElementById(`${
      
      name}`)
    if(elem) {
    
    
        let children = document.getElementById(`${
      
      name}`).children
        for(let i=0;i<children.length;i++) {
    
     // foreach报错
            children[i].style = `fill: ${
      
      color}`; // 这里不能用with语句,严格模式不支持with
        }
    }
  }

  // 允许自定义尺寸
  const setSize = () => {
    
    
    if(!size){
    
    
        setSvgSize({
    
    width:30,height:30})
        return
    }
    typeof size === "number" || "string" ? 
        setSvgSize({
    
    width:size,height:size}) : 
        (size.length && size.length === 1 ? 
            setSvgSize({
    
    width:size[0],height:size[0]}):
            setSvgSize({
    
    width:size[0],height:size[1]})
        )
  }

  // 根据name拿到svg路径
  const getSvg = async () => {
    
    
    console.log("getSvg")
    const svg = await import(`../../icons/svg/${
      
      name}.svg`)
    setSvgModule(svg)
  }
  const iconName = useMemo(() => {
    
    
    setColor() // 保证页面刷新时不会因为找不到id为name的标签而报错
    if (svgModule && svgModule.default) {
    
    
      return `#${
      
      svgModule.default.id}`
    }
  }, [svgModule])

  useMemo(() => {
    
    
    setSize()
  }, [size])

  useMemo( ()=>{
    
    
    setColor()
  },[color])


  useEffect(() => {
    
    
    getSvg() 
  }, [])

  return (
    <svg
    {
    
    ...svgSize}
    aria-hidden="true"
    >
      <use xlinkHref={
    
    iconName} />
    </svg>
  )
}

export default Icon

The effect of passing in color when using (app.js):

import React from 'react';
import Icon from "./components/icons"

function App() {
    
    
  return (
    <div>
      <p>test2</p>
      <Icon name="account" size="200" color="pink"/>
      <Icon name="pwd" color="green"/>
    </div>
  );
}

export default App;

insert image description here
The effect of not passing in color:

<div>
      <p>test2</p>
      <Icon name="account" size="200"/>
      <Icon name="pwd"/>
</div>

insert image description here

adaptation

The svg images I used to test were basically downloaded from iconfont, and the structure is relatively uniform. For example, the structure of the account.svg image only has the label of path:
insert image description here

However, sometimes in the project, some svg images provided by the designer have more layers and tags (for example, there are multiple tags such as rect and g), and the color attribute is not sure which tag’s fill is in. It is very likely that the svg cannot be changed like the above The color of the picture, so it is best to traverse each label, so the following processing is done to ensure that each layer is traversed:

   const setChildColor = (elem) => {
    
    
        const {
    
    children} = elem
        if(children) {
    
    
            for(let i=0;i<children.length;i++) {
    
    
                children[i].style = `fill:${
      
      color}`
                if(children[i].children) {
    
    
                    setChildColor(children[i])
                }
            }
        }
    }

  // 允许自定义颜色
  const setColor = () => {
    
    
    let elem = document.getElementById(`${
      
      name}`)
    if(elem) {
    
    
        setChildColor(elem)
    }
  }

Guess you like

Origin blog.csdn.net/Charonmomo/article/details/129788881