Home > Blockchain >  Make svg pattern content overflow from the frame of <pattern>, rather than being cut off
Make svg pattern content overflow from the frame of <pattern>, rather than being cut off

Time:11-12

I created a pattern with icons. Users can drag the slider to rotate the icons of the pattern. Here are my codes:

const slider = document.getElementById("slider")

const carrotIcon = document.getElementById("carrotIcon")

slider.oninput = function(){ 

  carrotIcon.setAttribute('transform', 'rotate(' slider.value ' 25 25) translate(0 0)')
}
<svg width="200px" height="150px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  
  
  <symbol id="carrot" viewBox="0 0 50 50">
        <rect width="50" height="50" fill="none" stroke="black" stroke-width="1"/>
    <path d="M30 13c-1-3-5-4-7-2h-1l1-11h-3l-1 9-4-7-3 1 5 8-8-4-1 2 9 5h-1c-2 2-3 5-2 7l2 2 5-3 1 2-5 3 8 9 5-3 2 2-5 3 12 14 3-2-8-25-5 3-1-2 5-3-3-8z" />

  </symbol>
  
  <pattern id="myPat" patternUnits="userSpaceOnUse" width="50" height="50" patternTransform="rotate(0)">
    <use id="carrotIcon" xlink:href="#carrot" width="50" height="50" fill="orange" transform="rotate(0 25 25) translate(0 0)"></use>
  </pattern>
  
  <rect x="0px" y="0px" width="300px" height="200px" fill="url(#myPat)"> </rect>
</svg>


<p style=font-size:15px>Rotate Icons </p>
<input id="slider" type="range" min="0" max="360" value="0" step='1' >

You can notice that when you rotate, the content of pattern will be cut off by the frame of <pattern>, because in some dimensions the content of the pattern is larger than the frame of the pattern.

current situation

I hope the content of the pattern can overflow from the pattern when rotating. They can overlap with each other. Here is a diagram of the expected result:

expected results

Do you know how to achieve it?

CodePudding user response:

Theoretically, this should be solved by adding a overflow="visible" to the <pattern> element. Unfortunately, you are running into an unresolved issue with conflicting browser implementations.

The SVG 2 specification says this:

SVG's user agent style sheet sets the overflow property for pattern elements to hidden, which causes a rectangular clipping path to be created at the bounds of the pattern tile. Unless the overflow property is overridden, any graphics within the pattern which goes outside of the pattern rectangle will be clipped...

Note that if the overflow property is set to visible the rendering behavior for the pattern outside the bounds of the pattern is currently undefined. A future version of SVG may require the overflow to be shown. SVG implementers are encouraged to render the overflow as this is the behavior expected by authors...

An open issue from 2016 about this explains:

The effect of visible overflow for pattern tiles was not clearly defined in SVG 1, leading to non-interoperable implementations...

This is not a theoretical issue. I get complaints & confusion from authors about why patterns that work just the way they want in one browser don't work in another. Specifically, the issue comes up when the repeating element of the pattern doesn't fit in a neat rectangular tile.

Here's a recent example screenshot. The top image is from Firefox, showing clipping to the pattern tile despite the overflow value, the bottom image is from Chrome...

My current best advice for the authors is to use <use> elements to create enough repetitions to completely fill in the rectangular tile. But that's far from ideal. Not only is it messy and repetitive code, it also opens up the possibility of edge effects in the rendering (revealing the edges of the pattern tiles).

Since then, nothing has really happened:

boggydigital added this to the SVG 2.1 Working Draft milestone on Jun 11, 2018

So you are stuck with entering multiple <use> elements in your <pattern>. Each one needs to rotate around its own rotation center. The best solution is to rotate the content of the <symbol> - which then needs its own overflow="visible" to work.

const slider = document.getElementById("slider")

const carrotIcon = document.getElementById("carrotIcon")

slider.oninput = function(){ 

  carrotIcon.setAttribute('transform', 'rotate(' slider.value ' 25 25) translate(0 0)')
}
<svg width="200px" height="150px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  
  
  <symbol id="carrot" viewBox="0 0 50 50" overflow="visible">
     <g id="carrotIcon">
        <rect width="50" height="50" fill="none" stroke="black" stroke-width="1"/>
        <path d="M30 13c-1-3-5-4-7-2h-1l1-11h-3l-1 9-4-7-3 1 5 8-8-4-1 2 9 5h-1c-2 2-3 5-2 7l2 2 5-3 1 2-5 3 8 9 5-3 2 2-5 3 12 14 3-2-8-25-5 3-1-2 5-3-3-8z" />
     </g>
  </symbol>
  
  <pattern id="myPat" patternUnits="userSpaceOnUse" width="50" height="50" style="overflow:visible">
    <use xlink:href="#carrot" width="50" height="50" fill="orange"></use>
    <use xlink:href="#carrot" x="-50" width="50" height="50" fill="orange"></use>
    <use xlink:href="#carrot" x="50" width="50" height="50" fill="orange"></use>
    <use xlink:href="#carrot" y="-50" width="50" height="50" fill="orange"></use>
    <use xlink:href="#carrot" y="50" width="50" height="50" fill="orange"></use>
  </pattern>
  
  <rect x="0px" y="0px" width="300px" height="200px" fill="url(#myPat)" overflow="visible"> </rect>
</svg>


<p style=font-size:15px>Rotate Icons </p>
<input id="slider" type="range" min="0" max="360" value="0" step='1' >

  • Related