Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use svg patterns in a cross browser consistent way?

Tags:

css

webkit

svg

I want a SVG image (prerendered, but inserted with js in an svg tag, to allow for some further manipulation) to be able to use a predefined pattern, using the "pattern" tag. Sounds simple enough, doesn't it? Well, turns out Chrome (Webkit?) behaves a bit different from any other browsers, and now I'm not sure what the best way would actually be to achieve this.

My svg looks like this:

<svg>
 <defs>
  <pattern id="specialPattern">...</pattern>
 </defs>
 <path class="special"></path>
</svg>

and I want paths with the class special to have "pattern" as fill.

Attempt one: Works in Chrome, not in FF or Opera

My first attempt was to simply put this in my css:

 .special { fill:url("#specialPattern");}

This actually works in Chrome, though when you think about it, it probably shouldn't. The other browsers I tried interpret this url as relative to the file it's in (the css file), which makes more sense.

Attempt two: Works in FF and Opera, not in Chrome

Next attempt: Provide an absolute url to the pattern.

 .special { fill:url("//example.com/styles/svgWithStyleDeclaration.svg#specialPattern");}

While this works as expected in FF and Opera, Chrome now resets the fill instead (I have no idea where it is actually looking for that style)

Attempt three: Works, kind of

Inlining the style in the SVG works everywhere it seems: style="fill:url('#specialPattern')"

And though I guess this is a case where the lines between content and presentation is blurred, in my case at least it would be much better to keep style decclarations elsewhere (not least because this would make my SVG need to be much bigger)

Attempt four: Works (?) but dirty

I haven't tested a lot of browsers, so I'm not sure about how water proof it is, but it seems to me like using a css hack to detect webkit browsers would work:

@media screen and (-webkit-min-device-pixel-ratio:0) {
  .special {fill: url("#specialPattern");}
}
 .special { fill:url("//example.com/styles/svgWithStyleDeclaration.svg#specialPattern");}

Now, there MUST be a more elegant way to solve this. How should it be done?

Edit: Turns out that IE behaves like Chrome here, so you would also need to make sure IE<=9 has 'fill: url(#specialPattern)'

like image 886
leo Avatar asked Dec 08 '13 21:12

leo


1 Answers

Here's a Fiddle that I did for manipulating patterns and masks. It's a ratings display in svg xml, in which I wanted to be able to use a percentage for the rating bar.

Fiddle: http://jsfiddle.net/cnLHE/296/

By changing the last line to "width="50", and pressing Run, you can see the rating bar resizes.

<svg width="100%" height="100%" viewBox="0 0 100 20" version="1.1">
<defs>
  <pattern id="pattern1" x="0" y="0" width="20" height="20"
         patternUnits="userSpaceOnUse" >
      <circle cx="10" cy="10" r="5" style="fill:white" />
   </pattern>
  <pattern id="pattern2" x="0" y="0" width="20" height="20"
         patternUnits="userSpaceOnUse" >
      <circle cx="10" cy="10" r="9" style="fill:white" />
      <circle cx="10" cy="10" r="7" style="fill:black" />
    </pattern>
  <mask id="mask1" x="0" y="0" width="100" height="20" >
    <rect x="0" y="0"  width="100" height="20"
        style="stroke:none; fill: url(#pattern2)"/>
  </mask>
  <mask id="mask5" x="0" y="0" width="100" height="20" >
    <rect x="0" y="0"  width="100" height="20"
        style="stroke:none; fill: url(#pattern1)"/>
  </mask>
</defs>1<rect x="0" y="0" width="500" height="20" fill="url(#pattern2)" style="fill:#2498c7; mask: url(#mask1)"/>
<rect x="0" y="0" width="50" height="20" style="fill:#2498c7; mask: url(#mask5)"/>

</svg>

I didn't have any cross browser issues, BUT, I did have issues with the SVG disappearing intermittently in grid layouts. In webkit with multiple instances in page, they weren't always showing.

Further info available at css-tricks: http://css-tricks.com/using-svg/

like image 64
Jennifer Michelle Avatar answered Nov 15 '22 04:11

Jennifer Michelle