Home > Mobile >  SVG - rotate <text> in relation to <rect>
SVG - rotate <text> in relation to <rect>

Time:07-26

I'm writing a legend for d3. Ten rectangles plotted from left to right. The outcome I would like is to position the text above its relevant cell, positioned vertically, but at a slight angle to the right. I have applied the rotation, but there's something about this that I'm missing, as it treats them as a group and rotates them all in a line, instead of rotating them relative to their sibling cell. enter image description here

Can someone recommend some attributes or style tips or perhaps a different manner of grouping the elements so that the text elements rotate individually around their own centers, and not as a single line?

Here's the current state of my code:

    <svg preserveAspectRatio="xMinYMin meet" viewBox={`0 0 800 70`}>
      <g transform={`translate(${[dms.marginTop, dms.marginLeft].join(',')})`}>
        <g>
          {range(10).map((d) => (
            <>
              <rect
                key={`${d}_legendCell`}
                width={cellSize - 1.5}
                height={cellSize - 1.5}
                fill={colorScale(Number(legendBands(String(d)))   interval)}
                x={d * cellSize}
              ></rect>
              <text
                key={`${d}_legendLabel`}
                fontWeight="300"
                fontSize="12"
                width="100"
                y={cellSize * d}
                transform="rotate(290)"
                dy=".85rem"
              >
                {Number(legendBands(String(d))).toFixed(1)}
              </text>
            </>
          ))}
        </g>
      </g>
    </svg>

TIA!

CodePudding user response:

If I read your code correctly you're currently aligning your <text> elements vertically and rotate them - better use horizontal offsets

You could instead place your label elements with specific x coordinates relative to your current rect's center.

Example 2nd cell

  <rect x="10" y="0" width="10" height="10" fill="magenta"/>
  <text x="15" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 15, 5)">02</text>

The text element's x-value (15) is the center point of the current cell/rect.
dominant-baseline="central" and text-anchor="middle" just simplify the horizontal and vertical alignment relative to the preceding <rect>.
We copy this x-value to the transformation:

transform="rotate(-45, 15, 5)"

This way we ensure the label is rotated around the rect's center.
Also, this method is quite robust, considering that some browsers still have problems with transform-origin and transform-box (especially some versions of Safari)

Static svg example

svg {
  display: block;
  border: 1px solid #ccc;
}

text {
  font-size: 5px
}
<svg width="50%" viewBox="0 0 30 10">
  <rect x="0" y="0" width="10" height="10" fill="green"/>
  <text x="5" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 5, 5)">01</text>
  
  <rect x="10" y="0" width="10" height="10" fill="magenta"/>
  <text x="15" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 15, 5)">02</text>
  
  <rect x="20" y="0" width="10" height="10" fill="cyan"/>
  <text x="25" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 25, 5)">03</text>
</svg>

<p>Add gaps</p>
<svg width="50%" viewBox="0 0 32 10">
  <rect x="0" y="0" width="10" height="10" fill="green"/>
  <text x="5" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 5, 5)">01</text>
  
  <rect x="11" y="0" width="10" height="10" fill="magenta"/>
  <text x="16" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 16, 5)">02</text>
  
  <rect x="22" y="0" width="10" height="10" fill="cyan"/>
  <text x="27" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 27, 5)">03</text>

</svg>

  • Related