I try this code (JSFiddle)
const Piece = () => {
return (
<svg>
<g transform="translate(XXX,XXX)">
<circle cx="-50" cy="-50" r="10" fill="black" />
</g>
</svg>
)
}
const root = ReactDOM.createRoot(document.getElementById("app"));
root.render(
<svg width="700" height="700" fill="#FFDDDD">
<g transform="translate(YYY,YYY)">
<Piece />
</g>
</svg>
);
where the XXX and YYY equals to 0 or 100, and I can see that:
if the translation takes place inside the component Piece (XXX=100, YYY=0), it works in the sense that the circle appears on the screen even if it is designed outside the screen initially (-50, -50);
if the translation takes place inside the main component (XXX=0, YYY=100), the circle doesn't appear.
I thought it was the same to translate inside or outside, but it is not. The thing is that I want to gather many different parts and it's not a good thing to transmit values of translation to all these parts...
Of course, I have tested the code on a true React (18) with d3 (7) structure with different files Piece, App, etc. and the result is the same.
What am I missing here?
If you write your SVG properly, then it doesn't matter which <g>
you use for that translation. In fact, you don't need a <g>
at all if you're transforming a single element because the "transform" attribute can be used on any SVG element unless you want to transform groups (hence the "g") of elements.
So: one <svg>
root, with a viewBox
attribute so the browser knows what it's actually rendering, and no nesting <svg>
inside <svg>
unless you absolutely have to (and if you have to ask when you might have to, then you don't). And you can't set a "fill" on the SVG root element, but you can use CSS for a background color, or you can create a <rect fill="..." ... />
with the appropriate dimensions.
svg { border: 1px solid black; }
<svg width="100" height="100" viewBox="0 0 100 100">
<g transform="translate(100,100)">
<circle cx="-50" cy="-50" r="10" fill="black" />
</g>
</svg>
<svg width="100" height="100" viewBox="0 0 100 100">
<circle cx="-50" cy="-50" r="10" fill="black" transform="translate(100,100)" />
</svg>
So if you turn that into something composable, your Piece
is just a circle (no need for a <g>
unless you want to place more than one, and then the <g>
goes around your collection of Piece
s):
const Piece = ({ tx=0, ty=0 }) => (
<circle cx="-50" cy="-50" r="10" fill="black" transform="translate({tx},{ty})"/>
);
// But you need viewBox, otherwise your SVG cannot be rendered correctly.
const SVG = ({ tx=0, ty=0 }) => (
<svg width="100" height="100" viewBox="0 0 100 100">
<Piece tx={tx} ty={ty}/>
</svg>
);
const root = ReactDOM.createRoot(document.getElementById("app"));
root.render(<SVG tx={100} ty={100}/>);