Search code examples
svg

How to keep an SVG gradient fixed relative to the screen while transforming a <g> group?


I have an SVG group that contains several shapes, and these shapes use a gradient defined with <g fill="url(#myGradient)"... . I want to apply transformations (e.g., scaling, rotation, translation) to the group, but I need the gradient to remain fixed relative to the screen (viewport).

Currently, when I apply transformations to the group, the gradient gets affected as well, even though I used gradientUnits="userSpaceOnUse". How can I ensure the gradient stays fixed in place and does not move or rotate with the transformed group?

<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="myGradient" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="200" y2="0">
      <stop offset="0%" stop-color="red"></stop>
      <stop offset="100%" stop-color="blue"></stop>
    </linearGradient>
  </defs>
  <g fill="url(#myGradient)" transform="translate(200, 200) rotate(0)">
    <rect x="0" y="0" width="300" height="80"></rect>
    <rect x="0" y="100" width="300" height="80"></rect>
  </g>
</svg>

with rotate(0) -> the blue color of the gradient is at right
with rotate(90) -> the blue color of the gradient is at bottom

What adjustments should I make to ensure the gradient stays fixed relative to the screen and does not transform along with the group?


Solution

  • Move the shapes to be rendered with the gradient in a <mask> element. Apply the gradient to a recttangle filling your complete canvas, and then cut out the shapes by applying the mask.

    <svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <linearGradient id="myGradient" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="200" y2="0">
          <stop offset="0%" stop-color="red"></stop>
          <stop offset="100%" stop-color="blue"></stop>
        </linearGradient>
        <mask id="shapes" fill="white">
          <g transform="translate(200, 200) rotate(90)">
            <rect x="0" y="0" width="300" height="80"></rect>
            <rect x="0" y="100" width="300" height="80"></rect>
          </g>
        </mask>
      </defs>
      <rect width="100%" height="100%" fill="url(#myGradient)" mask="url(#shapes)">
      </rect>
    </svg>