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状态,更灵活:
- 给外层SVG绑定CSS变量:
<svg ref={svgRef} style={{ '--svg-custom-color': colorFromStore } as React.CSSProperties} className="svg-icon" />
- 添加全局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在容器内正确缩放
核心实现步骤
- 给SVG外层容器设置固定尺寸,限制边界
- 确保SVG拥有
viewBox属性(无则自动生成) - 配置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
相关产品推荐
相关产品推荐

