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

48 Views Asked by At

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?

2

There are 2 best solutions below

0
chrwahl On BEST ANSWER

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>

0
Michael Mullany On

You can apply a gradientTransform to your gradient definition (and drop the userSpaceUnits) aka:

   <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
  <defs>
<linearGradient id="gradient1" x1="50%" y1="0" x2="50%" y2="100%"  gradientTransform ="rotate(-45) translate(0 0.2)">
  <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)" transform="rotate(45, 50, 50)"
  />
</svg>