SVG gradient on rotated elements

47 Views Asked by At

I'm trying to apply a gradient fill over a few rects rotated to form a circle. The final result would be something like this
enter image description here
but with individual, disconnected pieces. The issue I'm facing is that all of the rects are filled the same.

Here's what i've tried, this is just a minimal example:

<svg width=300px height=300px viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
    <defs>

        <linearGradient id="notchGradient" x1="0" y1="0" x2="100%" y2="100%" gradientUnits="userSpaceOnUse">
            <stop offset="0%" style="stop-color:blue;stop-opacity:1" />
            <stop offset="50%" style="stop-color:green;stop-opacity:1" />
            <stop offset="100%" style="stop-color:red;stop-opacity:1" />
        </linearGradient>
    </defs>
    <g transform="translate(50,50)" fill="url(#notchGradient)">
    <g>
        <g transform="rotate(0)">
            <rect x="0" y="-50" width="2" height="5"/>
        </g>
        <g transform="rotate(90)">
            <rect x="0" y="-50" width="2" height="5"/>
        </g>
        <g transform="rotate(180)">
            <rect x="0" y="-50" width="2" height="5"/>
        </g>
        <g transform="rotate(270)">
            <rect x="0" y="-50" width="2" height="5"/>
        </g>
    </g>
</svg>

I'm assuming they're the same because they all have 0,-50 coords even though they're at different spots because of the rotation.

So i've also tried putting them at poins around the circle using just coords:

<svg width=300px height=300px overflow=visible viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
    <defs>
        <linearGradient id="notchGradient" x1="0" y1="0" x2="100%" y2="100%" gradientUnits="userSpaceOnUse">
            <stop offset="0%" style="stop-color:blue;stop-opacity:1" />
            <stop offset="50%" style="stop-color:green;stop-opacity:1" />
            <stop offset="100%" style="stop-color:red;stop-opacity:1" />
        </linearGradient>
    </defs>
    <g fill="url(#notchGradient)">
        <g transform="rotate(0)" style="transform-box:fill-box;transform-origin:center;">
            <rect x="50" y="0" width="2" height="5"/>
        </g>
        <g transform="rotate(90)" style="transform-box:fill-box;transform-origin:center;">
            <rect x="100" y="50" width="2" height="5"/>
        </g>
        <g transform="rotate(180)" style="transform-box:fill-box;transform-origin:center;">
            <rect x="50" y="100" width="2" height="5"/>
        </g>
        <g transform="rotate(270)" style="transform-box:fill-box;transform-origin:center;">
            <rect x="0" y="50" width="2" height="5"/>
        </g>
    </g>
</svg>

This does seem to apply the gradient, but the problem now is that i somehow need to figure out coords for each one, and if i wanna do one every few degrees this doesn't seem reasonable.

1

There are 1 best solutions below

2
Michael Mullany On

Fills are applied before transforms, so you can't do what you want with the current structure (it doesn't matter that the fill is defined in the outer g: the fill is inherited by the rect and painted before the transform is applied). You can either convert everything to use a filter - which is applied after a transform, or you can explicitly position each rect and then use a corrected gradient definition - like so:

<svg width="300px" height="300px" viewBox="0 0 120 120" >
    <defs>
        <linearGradient id="notchGradient" x1="0" y1="0" x2="120" y2="120" gradientUnits="userSpaceOnUse">
            <stop offset="0%" style="stop-color:blue;stop-opacity:1" />
            <stop offset="50%" style="stop-color:green;stop-opacity:1" />
            <stop offset="100%" style="stop-color:red;stop-opacity:1" />
        </linearGradient>
    </defs>
    <g fill="url(#notchGradient)">
       <rect x="50" y="0" width="12" height="15"/>
       <rect x="100" y="50" width="15" height="12"/>
       <rect x="50" y="100" width="12" height="15"/>
       <rect x="0" y="50" width="15" height="12"/>
        </g>
    </g>
</svg>

Or if you want to use a filter:

<svg width="300px" height="300px" viewBox="0 0 140 140" >
<defs>
     <linearGradient id="notchGradient" x1="0" y1="0" x2="140" y2="140" gradientUnits="userSpaceOnUse">
     <stop offset="0%" style="stop-color:blue;stop-opacity:1" />
     <stop offset="50%" style="stop-color:green;stop-opacity:1" />
     <stop offset="100%" style="stop-color:red;stop-opacity:1" />
   </linearGradient>
  
     <rect id="fullBleed" x="0" y="0" width="140" height="140" fill="url(#notchGradient)" > 
       
      <filter id="gradientMe">
        <feImage x="0" y="0" xlink:href="#fullBleed"/>
        <feComposite operator="in" in2="SourceGraphic"/>
      </filter>
    </defs>
      
    <g filter = "url(#gradientMe")>
      <g transform="rotate(0 70 70)">
       <rect x="50" y="0" width="42" height="25"/>
       <rect x="100" y="50" width="45" height="22"/>
       <rect x="50" y="100" width="42" height="25"/>
       <rect x="0" y="50" width="45" height="22"/>
      </g>
        </g>
    </g>
</svg>