You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中如何为动态获取的SVG注入颜色并控制尺寸?

一、动态注入自定义颜色

方案1:遍历SVG元素直接修改属性

获取SVG后,递归遍历所有内部元素,替换非none的fill和stroke属性值,直接可控:

const fetchSvgIcon = async (url: string) => {
    try {
        const response = await axios.get(`https://corsproxy.io/?${url}`, {
            headers: {
                'Content-Type': 'application/xhtml+xml; charset=UTF-8',
            },
            responseType: 'text',
        });
        const parser = new DOMParser();
        const xmlDoc = parser.parseFromString(response.data, 'text/xml');
        const svg = xmlDoc.getElementsByTagName('svg')[0];
        if (svg && svgRef.current) {
            // 递归替换颜色
            const replaceColor = (element: Element) => {
                if (element.getAttribute('fill') && element.getAttribute('fill') !== 'none') {
                    element.setAttribute('fill', colorFromStore);
                }
                if (element.getAttribute('stroke') && element.getAttribute('stroke') !== 'none') {
                    element.setAttribute('stroke', colorFromStore);
                }
                Array.from(element.children).forEach(replaceColor);
            };
            replaceColor(svg);
            svgRef.current.innerHTML = svg.innerHTML;
        }
    } catch (error) {
        console.error("Error fetching SVG:", error);
    }
};

方案2:使用CSS变量(支持颜色动态更新)

无需重新请求SVG,通过CSS变量联动Redux状态,更灵活:

  1. 给外层SVG绑定CSS变量:
<svg 
  ref={svgRef} 
  style={{ '--svg-custom-color': colorFromStore } as React.CSSProperties}
  className="svg-icon"
/>
  1. 添加全局CSS:
.svg-icon * {
  fill: var(--svg-custom-color, currentColor) !important;
  stroke: var(--svg-custom-color, currentColor) !important;
}
/* 保留原SVG的none属性 */
.svg-icon [fill="none"] {
  fill: none !important;
}
.svg-icon [stroke="none"] {
  stroke: none !important;
}

二、确保SVG在容器内正确缩放

核心实现步骤

  1. 给SVG外层容器设置固定尺寸,限制边界
  2. 确保SVG拥有viewBox属性(无则自动生成)
  3. 配置SVG缩放规则,保持比例不溢出

优化后代码示例

// 外层容器+SVG结构
<div className="svg-wrapper">
  <svg ref={svgRef} className="svg-icon" />
</div>

// CSS样式
.svg-wrapper {
  width: 48px; /* 自定义固定尺寸 */
  height: 48px;
  overflow: hidden;
}

.svg-icon {
  width: 100%;
  height: 100%;
  preserveAspectRatio: xMidYMid meet; /* 居中缩放,不溢出容器 */
}

处理无viewBox的SVG

如果原SVG没有viewBox,基于其width/height自动生成:

if (svg && svgRef.current) {
  // 自动生成viewBox
  if (!svg.getAttribute('viewBox')) {
    const width = svg.getAttribute('width') || '24';
    const height = svg.getAttribute('height') || '24';
    svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
  }
  // 同步viewBox到外层SVG
  svgRef.current.setAttribute('viewBox', svg.getAttribute('viewBox')!);
  // 后续颜色处理...
}

完整优化组件代码

import { useRef, useEffect } from 'react';
import { useSelector } from 'react-redux';
import axios from 'axios';
import { getUserColor } from './path-to-your-selectors';

const SvgIcon = ({ url }: { url: string }) => {
  const svgRef = useRef<SVGSVGElement>(null);
  const colorFromStore = useSelector(getUserColor);

  const fetchSvgIcon = async () => {
    try {
      const response = await axios.get(`https://corsproxy.io/?${url}`, {
        headers: {
          'Content-Type': 'application/xhtml+xml; charset=UTF-8',
        },
        responseType: 'text',
      });
      const parser = new DOMParser();
      const xmlDoc = parser.parseFromString(response.data, 'text/xml');
      const svg = xmlDoc.getElementsByTagName('svg')[0];
      if (svg && svgRef.current) {
        // 处理viewBox
        if (!svg.getAttribute('viewBox')) {
          const width = svg.getAttribute('width') || '24';
          const height = svg.getAttribute('height') || '24';
          svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
        }
        svgRef.current.setAttribute('viewBox', svg.getAttribute('viewBox')!);

        // 替换颜色
        const replaceColor = (element: Element) => {
          if (element.getAttribute('fill') && element.getAttribute('fill') !== 'none') {
            element.setAttribute('fill', colorFromStore);
          }
          if (element.getAttribute('stroke') && element.getAttribute('stroke') !== 'none') {
            element.setAttribute('stroke', colorFromStore);
          }
          Array.from(element.children).forEach(replaceColor);
        };
        replaceColor(svg);

        svgRef.current.innerHTML = svg.innerHTML;
      }
    } catch (error) {
      console.error("Error fetching SVG:", error);
    }
  };

  useEffect(() => {
    fetchSvgIcon();
    // 颜色变化时更新现有SVG颜色
    if (svgRef.current) {
      const updateColor = (element: Element) => {
        if (element.getAttribute('fill') && element.getAttribute('fill') !== 'none') {
          element.setAttribute('fill', colorFromStore);
        }
        if (element.getAttribute('stroke') && element.getAttribute('stroke') !== 'none') {
          element.setAttribute('stroke', colorFromStore);
        }
        Array.from(element.children).forEach(updateColor);
      };
      Array.from(svgRef.current.children).forEach(updateColor);
    }
  }, [url, colorFromStore]);

  return (
    <div className="svg-wrapper">
      <svg ref={svgRef} className="svg-icon" />
    </div>
  );
};

export default SvgIcon;

内容的提问来源于stack exchange,提问作者bo bo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 06:04:54