Search code examples
reactjsd3.jstranslation

Is it equivalent to translate inside the component or the component itself?


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?


Solution

  • 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 Pieces):

    
    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}/>);