Next.js 13中如何修改SVG属性值并添加节点?
在Next.js 13中使用SVGR修改SVG内容与添加节点
问题背景
我在Next.js 13项目里用SVGR把SVG作为组件加载,代码如下:
import CvSvg from './../../public/image.svg' export default function Home() { return ( <div className="flex flex-col min-h-screen"> <CvSvg /> </div> ) }
SVG里包含如下tspan元素:
<tspan sodipodi:role="line" style="font-size:4.93889px;fill:#000000;stroke:none;stroke-width:0.264583;stroke-opacity:1" x="79.525864" y="29.664894" id="tspan59991">message </tspan>
我想实现两个需求:一是修改该tspan的文本内容、位置、颜色等属性;二是在tspan节点后添加新元素(比如一条线)。我尝试了DOM操作的代码,但线条并未显示:
import { useEffect, useState } from 'react'; import SVG from './public/image.svg' function Home() { const [svgDoc, setSvgDoc] = useState(null); useEffect(() => { fetch(SVG) .then(response => response.text()) .then(data => { const parser = new DOMParser(); const svgDoc = parser.parseFromString(data, "image/svg+xml"); const text = svgDoc.querySelector('#tspan59991'); const line = svgDoc.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', '79.525864'); line.setAttribute('y1', '30.664894'); line.setAttribute('x2', '90'); line.setAttribute('y2', '30.664894'); line.setAttribute('style', 'stroke:#000000;stroke-width:1'); text.parentNode.appendChild(line); setSvgDoc(svgDoc.documentElement.outerHTML); }); }, []); return ( <div className="flex flex-col min-h-screen"> <div dangerouslySetInnerHTML={{ __html: svgDoc }} /> </div> ); } export default Home;
解决方案
一、通过SVGR组件特性修改(推荐)
SVGR会将SVG转换为React组件,你可以直接利用组件的props或ref进行操作,无需复杂的DOM解析:
1. 修改tspan的文本、颜色与位置
import { useRef, useEffect } from 'react'; import CvSvg from './../../public/image.svg'; export default function Home() { const tspanRef = useRef(null); useEffect(() => { if (tspanRef.current) { // 修改文本内容 tspanRef.current.textContent = "新的自定义文本"; // 修改文字颜色(覆盖原style的fill属性) tspanRef.current.style.fill = "#ff4444"; // 修改位置坐标 tspanRef.current.setAttribute('x', '85'); tspanRef.current.setAttribute('y', '35'); } }, []); return ( <div className="flex flex-col min-h-screen"> {/* 通过svgRef获取SVG内部元素的引用 */} <CvSvg svgRef={(svgElement) => { if (svgElement) tspanRef.current = svgElement.querySelector('#tspan59991'); }} /> </div> ); }
2. 给SVG添加新节点(比如线条)
直接通过组件的children插槽插入新的SVG元素,或者用createPortal指定插入位置:
import { useRef } from 'react'; import { createPortal } from 'react-dom'; import CvSvg from './../../public/image.svg'; export default function Home() { const tspanParentRef = useRef(null); return ( <div className="flex flex-col min-h-screen"> <CvSvg svgRef={(svgElement) => { if (svgElement) tspanParentRef.current = svgElement.querySelector('#tspan59991').parentNode; }} /> {/* 使用createPortal将线条插入到tspan的父节点下 */} {tspanParentRef.current && createPortal( <line x1="79.525864" y1="30.664894" x2="90" y2="30.664894" style={{ stroke: "#000000", strokeWidth: 1 }} />, tspanParentRef.current )} </div> ); }
二、修复你尝试的DOM操作代码
你之前的代码线条不显示,主要是因为使用outerHTML转换丢失了部分SVG上下文,直接将解析后的SVG元素插入DOM即可:
import { useEffect, useRef } from 'react'; import SVG from './public/image.svg'; function Home() { const svgContainerRef = useRef(null); useEffect(() => { fetch(SVG) .then(response => response.text()) .then(data => { const parser = new DOMParser(); const svgDoc = parser.parseFromString(data, "image/svg+xml"); const text = svgDoc.querySelector('#tspan59991'); const line = svgDoc.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', '79.525864'); line.setAttribute('y1', '30.664894'); line.setAttribute('x2', '90'); line.setAttribute('y2', '30.664894'); line.setAttribute('stroke', '#000000'); line.setAttribute('stroke-width', '1'); text.parentNode.appendChild(line); // 直接将SVG元素添加到容器,而非转换为HTML字符串 svgContainerRef.current.appendChild(svgDoc.documentElement); }); }, []); return ( <div className="flex flex-col min-h-screen"> <div ref={svgContainerRef} /> </div> ); } export default Home;
内容的提问来源于stack exchange,提问作者Qunther
相关产品推荐
相关产品推荐

