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

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)'

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/

Related

svg - keep fill texture original size when resizing a path

I define a svg <pattern> like this:
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id="circles-1_4" patternUnits="userSpaceOnUse" width="10" height="10">
<image xlink:href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIxMHB4IiBoZWlnaHQ9IjEwcHgiIHZpZXdCb3g9IjAgMCAxMCAxMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTAgMTAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxjaXJjbGUgY3g9IjEuMjUiIGN5PSIxLjI1IiByPSIxLjI1Ii8+PHJlY3QgZmlsbD0ibm9uZSIgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIi8+PC9zdmc+"
x="0" y="0" width="10" height="10">
</image>
</pattern>
</defs>
</svg>
visually, this is the equivalent of this:
So then I call my css for svg as a fill like this:
svg #VISUEL-3 * {fill: url(#circles-1);}
I get a pretty good result:
But when I display my graphic smaller (1/4 in this i.e), the fill adapt like this
It's difficult to see on the screenshots because the scale is broken due to the width 100% of stackoverflow but the vector-effect:non-scaling-stroke works perfectly so the strokes have the same size between the first and the second screenshot and the number "1,2,3,4,5,6" as well.
So as you can see the fill has adapted...
Is it possible to keep the same pattern size (same size of dots) like in css? That look messy visually when I have two graphics that are not of the same size and are next to each other.
Is my method right to obtain that? (I'm ready to change my method..)
The pattern elements are applied to the referencing element before transformations on the element or one of its parents are applied - so the pattern size gets transformed as well. The only way to counteract this is to write a seperate pattern element for each scale you us it at, including a patternTransform with the inverse scale. Fortunately, there is a mechanism for cloning patterns with a xlink:href attribute.
.simple {
fill: url(#dots);
}
.quarter {
fill: url(#quadrupleDots);
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="150">
<defs>
<pattern id="dots" patternUnits="userSpaceOnUse" width="10" height="10">
<circle r="1.25" cx="1.25" cy="1.25" />
</pattern>
<pattern id="quadrupleDots" xlink:href="#dots" patternTransform="scale(4)" />
</defs>
<rect id="shape" class="simple" x="20" y="20" width="100" height="100" />
<rect class="quarter" x="800" y="80" width="100" height="100" transform="scale(0.25)" />
</svg>

is there any way to use an svg (img) to crop an image?

As far as I understand (by reading previously posted questions on stackoverflow) that cropping an image using svg coorindates is possible, but it is possible to crop an image by using an svg link? for example: <img src="http://imgh.us/face01.svg">
EDIT: This is what I meant:
Thanks!
Yes. You can do it using the mask-image property. This is supported on all browsers except IE.
.masked {
-webkit-mask-image: url(http://imgh.us/face01.svg);
mask-image: url(http://imgh.us/face01.svg);
}
<img src="http://lorempixel.com/400/400" width="400" height="400" class="masked">
Unfortunately you will still need to make other arrangements for IE.
You can do it all inside SVG using the image element combined with a SVG mask or a SVG filter and have it work on IE10+ (and all other browsers). Here is the filter example:
<svg width="400px" height="400px">
<defs>
<filter id="crop-me" x="0%" y="0%">
<feImage xlink:href="http://imgh.us/face01.svg" result="area"/>
<feComposite operator="in" in="SourceGraphic" in2="area"/>
</filter>
</defs>
<image filter="url(#crop-me)" xlink:href="http://lorempixel.com/400/400" x="0" y="0" width="200" height="300"/>
</svg>

SVG rendering blurry in all browsers except chrome

I have an SVG file that is using a pattern to make the background of a schedule. In Google Chrome, this renders as expected, with every line not being anti-aliased. However, in Firefox, Safari, and Internet Explorer, it renders with every line being 2px that are semi-transparent.
What I Tried
After many hours of searching and trying different approaches, I have come to the following conclusions:
The blurriness is in part caused by the fact that the svg has a dynamic width and is solved when a viewBox attribute is applied, however this looses all scalability which defeats the purpose of using an svg.
When there is only one svg on the page, it appears to look normal (or close enough to normal), however when there are multiple in succession, every other one (or sometimes randomly) will appear with each 1px line appearing as 2px anti-aliased line (looks bad).
Offsetting the x and/or y-values by .5 pixels does not change anything and can make the rendering look even worse sometimes.
Adding shape-rendering: crispEdges; to the style of each line can either a) make the lines disappear completely or just not have any effect whatsoever or b) make the colors in the 2px lines darker, but not actually making the lines 1 pixel.
The enable-background attribute appears to have no effect on the
Again, none of these problems appear in Chrome, but do in all other modern browsers
The SVG in question
<svg width="100%" height="500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="day" width="100%" x="0" height="40" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="100%" y2="0" style="stroke:#bbb;stroke-width:1" />
<line x1="0" y1="20" x2="100%" y2="20" style="stroke:#ccc;stroke-width:1" stroke-dasharray="3,3" />
</pattern>
<pattern id="hours" width="100%" x="0" height="40" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="100%" y2="0" style="stroke:#000;stroke-width:1" />
</pattern>
</defs>
<rect x="0" width="5%" height="500" fill="url(#hours)" />
<rect x="7%" width="17%" height="500" fill="url(#day)" />
<rect x="26%" width="17%" height="500" fill="url(#day)" />
<rect x="45%" width="17%" height="500" fill="url(#day)" />
<rect x="64%" width="17%" height="500" fill="url(#day)" />
<rect x="83%" width="17%" height="500" fill="url(#day)" />
</svg>
Images of Renderings
VIEW FULLSCREEN TO SEE EFFECT
Chrome (reference):
http://i.imgur.com/BMjFmH6.png
Firefox (with shape-rendering: crispEdges;):
http://i.imgur.com/4cgZjq7.png
Firefox (with .5px renderings, after multiple included svg's. Note, image rendered at different size than previous ones.)
(Can't post 3 links, but use this id after imgur.com):
JiuswRF.png
Any help on this problem would be greatly apreciated.
Thanks!
If you want to stay with pattern, I'd recommend rewriting all those units in absolute OR relative units, or to dynamically switch your viewBox dimensions in JavaScript.
An alternative is to generate a pattern using Filters. This displays crisply in Chrome and Firefox (although IE is still buggy)
<filter id="better" primitiveUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
<feFlood x="0%" y="0%" width="100%" height="0.2%" flood-color="#B00" result="redline"/>
<feFlood x="0%" y="3.9%" width="1%" height="0.2%" flood-color="#0B0" result="stroke-dash"/>
<feFlood x="0%" y="3.9%" width="2%" height="0.2%" flood-color="#FFF" result="stroke-dash2"/>
<feComposite operator="over" in="stroke-dash" in2="stroke-dash2" result="full-dash"/>
<feTile in="full-dash" x="0%" y="3.9%" width="100%" height="0.1%" result="green-stroke"/>
<feComposite x="0%" y="0%" height="8%" width="100%" operator="over" in="redline" in2="green-stroke" result="one-tile"/>
<feTile in="one-tile" x="0%" y="0%" height="100%" width="100%"/>
</filter>
http://codepen.io/mullany/pen/CLsxv

SVG : <use> not cascade css with chrome or IE10

I would like to use <use> on my document but I have problem with cascading classes.
I have the following svg:
<svg>
<defs>
<rect id="pattern" class="color-pattern" x="25" y="25" width="50" height="50"/>
</defs>
<g class="color-object">
<use xlink:href="#pattern"/>
</g>
</svg>
with the following css :
.color-pattern {fill:red;}
.color-object .color-pattern {fill:blue;}
JsFiddle
With firefox the rectangle is blue (I seem it's right), with IE10 or Chrome, the rectangle is red.
Is it a issue ? How I can proceed for having a right result on three browsers ?
Thanks for your answers and sorry for my english.

Scaling SVG rect on hover using CSS transform

It seems that applying the same CSS transform on hover to an HTML or SVG element does not create the same effect. As you can see in the following fiddle, the p and rect do not behave the same way while scaling, and that is precisely my problem.
http://jsfiddle.net/rKD7T/2/
How could I make the rect behave exactly as the p and scale properly?
I tried scaling the rect with a matrix - taking into account the origin point - but it did not seem to work either, or I did it wrong.
I'd like to stick to CSS solutions here but JS ones might also be an option.
Thanks for your help.
This seems about right.
<div>
<p></p>
<p></p>
<p></p>
<p></p>
</div>
<svg>
<g transform="translate(20, 40)">
<rect x="-20" y="-40" width="50" height="100"/>
<g transform="translate(55, 0)" >
<rect x="-20" y="-40" width="50" height="100"/>
</g>
<g transform="translate(110, 0)" >
<rect x="-20" y="-40" width="50" height="100"/>
</g>
<g transform="translate(165, 0)" >
<rect x="-20" y="-40" width="50" height="100"/>
</g>
</g>
</svg>
It works in Firefox trunk but doesn't seem to work with Firefox 16. I haven't tested Firefox Beta or Aurora to see in exactly when it's fixed.

Resources