svgsvg-pattern

Rotation invariant svg patterns


The patternUnits property of svg patterns seems to allow defining which coordinate space to use for pattern positions. "userSpaceOnUse" will use the global coordinate system for the pattern position.

I am looking for a similar property for rotation. I want all objects filled with the pattern hash to be rotated by 45° globally. In the standard implementation the rotation of the pattern is also affected.

Here is an example where it does not work:

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
  <defs>
    <pattern id="hashPattern" patternUnits="userSpaceOnUse" width="10" height="10">
      <path d="M 0 0 L 10 10" stroke="red" stroke-width="2" />
    </pattern>

    <g id="yourShape">
      <ellipse cx="25" cy="25" rx="15" ry="25" stroke="black" fill="url(#hashPattern)" />
    </g>
  </defs>

  <use href="#yourShape" x="20" y="20" />
  <use href="#yourShape" x="100" y="20" transform="rotate(45 125 45)" />
  <use href="#yourShape" x="20" y="100" transform="rotate(90 45 125)" />
  <use href="#yourShape" x="100" y="100" transform="rotate(180 125 125)" />
</svg>

Instead of the red lines showing into different directions I need all of the lines to show into the same correction.


Solution

  • Patterns are relative to the shape, so they will rotate with the shape.

    The best you can do is duplicate the <pattern> and apply a patternTransform and link that up to each shape. I changed the stroke for each pattern so they are easier to distinguish.

    Note: If you add a definition for a hash path shape, you can reference it with use in the duplicated patterns.

    <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
      <defs>
        <!-- Actual pattern "shape" -->
        <g id="hashPath">
          <path d="M 0 0 L 10 10" stroke-width="2" />
        </g>
    
        <!-- Create various patterns that reference the shape above and rotate -->
        <pattern id="hashPattern" patternUnits="userSpaceOnUse" width="10" height="10">
          <use href="#hashPath" stroke="red" />
        </pattern>
        <pattern id="hashPattern-45" patternUnits="userSpaceOnUse" width="10" height="10" patternTransform="rotate(-45)">
          <use href="#hashPath" stroke="blue" />
        </pattern>
        <pattern id="hashPattern-90" patternUnits="userSpaceOnUse" width="10" height="10" patternTransform="rotate(-90)">
          <use href="#hashPath" stroke="green" />
        </pattern>
        <pattern id="hashPattern-180" patternUnits="userSpaceOnUse" width="10" height="10" patternTransform="rotate(-180)">
          <use href="#hashPath" stroke="fuchsia" />
        </pattern>
    
        <g id="yourShape">
          <ellipse cx="25" cy="25" rx="15" ry="25" stroke="black" />
        </g>
      </defs>
    
      <!-- Apply the approiate rotated pattern for each rotation -->
      <use href="#yourShape" x="20" y="20" fill="url(#hashPattern)"/>
      <use href="#yourShape" x="100" y="20" transform="rotate(45 125 45)" fill="url(#hashPattern-45)" />
      <use href="#yourShape" x="20" y="100" transform="rotate(90 45 125)" fill="url(#hashPattern-90)" />
      <use href="#yourShape" x="100" y="100" transform="rotate(180 125 125)" fill="url(#hashPattern-180)" />
    </svg>

    As herrstrietzel, mentioned in the comments below, you could create <pattern> definitions that reference a base pattern using href, similar to <use> elements.

    The caveat being that the reference pattern is baked into each rotated copy. You no longer have control of the attributes of the pattern shape like in the version above.

    <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
      <defs>
        <pattern id="hashPattern" patternUnits="userSpaceOnUse" width="10" height="10">
          <path d="M 0 0 L 10 10" stroke-width="2" stroke="red" />
        </pattern>
        <pattern href="#hashPattern" id="hashPattern-45" patternTransform="rotate(-45)" />
        <pattern href="#hashPattern" id="hashPattern-90" patternTransform="rotate(-90)" />
        <pattern href="#hashPattern" id="hashPattern-180" patternTransform="rotate(-180)" />
    
        <g id="yourShape">
          <ellipse cx="25" cy="25" rx="15" ry="25" stroke="black" />
        </g>
      </defs>
    
      <use href="#yourShape" x="20" y="20" fill="url(#hashPattern)" />
      <use href="#yourShape" x="100" y="20" transform="rotate(45 125 45)" fill="url(#hashPattern-45)" />
      <use href="#yourShape" x="20" y="100" transform="rotate(90 45 125)" fill="url(#hashPattern-90)" />
      <use href="#yourShape" x="100" y="100" transform="rotate(180 125 125)" fill="url(#hashPattern-180)" />
    </svg>