Search code examples
svgsvg-filterssvg-transforms

How can I transform an SVG path without transforming applied gradient/filter?


Given some simple SVG like:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="gradient1" x1="50%" y1="0" x2="50%" y2="100%" gradientUnits="userSpaceOnUse">
      <stop offset="0%" style="stop-color:rgb(255,0,0); stop-opacity:1" />
      <stop offset="50%" style="stop-color:rgb(255,0,0); stop-opacity:1" />
      <stop offset="50%" style="stop-color:rgb(0,255,0); stop-opacity:1" />
    </linearGradient>
  </defs>

  <rect x="25" y="25" width="50" height="50"
    fill="url(#gradient1)"
  />
</svg>

which renders a square with a vertical, two-color gradient like so:

square with vertical, two-color gradient

I would like to be able to rotate the square without rotating the gradient. In other words, I’d like the gradient to use global (user space) coordinates independent of the path coordinates so that the render looks like so:

diamond with vertical, two-color gradient

But applying a transform to the path doesn’t work this way. When I give the path a transform like so:

  <rect x="25" y="25" width="50" height="50"
    fill="url(#gradient1)"
    transform="rotate(45, 50, 50)"
  />

It seems like it first renders the shape with its gradient, and then applies the transform such that the gradient is transformed with the path:

enter image description here

Is there any way to apply the gradient to the path after any transformations?


Solution

  • Another option would be to use a mask (or clip path) to mask off the rotated rectangle. Let's say that you would like to animate the rotation, then this solution would be easier to handle.

    <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <linearGradient id="gradient1" x1="50%" y1="0" x2="50%" y2="100%">
          <stop offset="0%" style="stop-color:rgb(255,0,0); stop-opacity:1" />
          <stop offset="50%" style="stop-color:rgb(255,0,0); stop-opacity:1" />
          <stop offset="50%" style="stop-color:rgb(0,255,0); stop-opacity:1" />
        </linearGradient>
        <mask id="m1">
          <rect x="25" y="25" width="50" height="50" fill="white" transform="rotate(45, 50, 50)"/>
        </mask>
      </defs>
      <rect width="100" height="100" fill="url(#gradient1)" mask="url(#m1)"/>
    </svg>