Search code examples
javascriptreactjssvgrotation

JS React - differing rotation in svg components


Seeking advice on working with svg components and their rotation.

I have a react functional component where I can add figures, move them around the screen and rotate them.

Those figures are implemented as svgs. Example:

    <svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" transform={`rotate(${props.rotation})`}>
        <ellipse style={{ "fill": "rgb(216, 216, 216)", "stroke": "rgb(0, 0, 0)" }} cx="250" cy="250" rx="125" ry="125" />
        <text className='unselectable' style={{ "whiteSpace": "pre", "fill": "rgb(51, 51, 51)", "fontFamily": "Arial, sans-serif", "font-size": "152.2px" }} x="166.561" y="303.035" transform="matrix(0.66583, 0, 0, 0.739777, 79.047778, 60.064947)">{props.tableName}</text>
    </svg>

So I handle the rotation by calculating the number and applying it in the transform of the svg. The problem is, that the text components also rotates and it becomes unreadable.

I tried to apply a negative degree to the text component but that does not work properly as the text drifts away (see screenshot).

Is there any solution to make the text not rotate while everything else in the svg rotates?

EDIT:

Forgot to attach this screenshot to the initial post. If you look at the < svg > rotation variant (3rd column) - the rects/ellipse rotates as by design, but the text should not rotate with it. enter image description here

Following the advice in the comments: I put the ellipse in a < g > and gave it rotation, while the text was outside of g.

enter image description here

Then I put both in < g > with rotation and gave < text > neg rotation.

<g transform={`rotate(${props.rotation})`}>
                <ellipse style={{ "fill": "rgb(216, 216, 216)", "stroke": "rgb(0, 0, 0)" }} cx="250" cy="250" rx="125" ry="125" />

                <text className='unselectable' style={{ "whiteSpace": "pre", "fill": "rgb(51, 51, 51)", "fontFamily": "Arial, sans-serif", "font-size": "152.2px" }} x="166.561" y="303.035" transform={`matrix(0.66583, 0, 0, 0.739777, 79.047778, 60.064947) rotate(-${props.rotation})`}>{props.tableName}</text>
</g>

Both ellipse and text rotate at different rates and they can go outside of border.

enter image description here

My main problem (which I failed to describe) is that I need the elements (e.g. ellipse) to rotate in place (as you can see in the screenshots - inside the bordered box. But I want the text inside not to rotate with it.


Solution

  • I have solved my issue, which I failed to describe (not fully understanding svgs and transform did not allow for that to happen.

    The solution is close to the one proposed by @normalcepalin.

    The main issue was: I had an svg component, containing:

    <svg>
    <lots of things>
    <text>
    </svg> 
    

    And I was applying rotation to the whole svg. When I tried the normalcepalin method, the rotation was kind of strange and, as far as I understand now, it was so because everything in

    <lots of things> 
    

    was offset from the origin point of the svg.

    This offset was also the problem when trying to rotate a

    <g>
    <lots of things>
    </g>
    

    and not rotating the text component. Everything started rotating off the view box.

    The final solution (and don't know if it's the best solution, but worked for me):

    <svg>
      <g transform={`rotate(${props.rotation})`} style={{ "transform-origin": "50% 50%" }}>
        <lots of things>
      </g>
      <g>
        <text> </text>
      </g>
    </svg>
    

    This allows for the text to remain stationary, but for all the stuff inside the first group to rotate in it's place. The addition of transform-origin as a css parameter without a particular rotation origin coordinates allows to easily rotate the object in it's own place.

    Again, I am not sure this is a best practice approach, but it worked for me.