Chrome's way of rounding sub-pixel values in SVGs creates a problem for me.
Is there any way to fix this problem? Because I use opacity I'm not able to simply add a dark background to remove the white glimps.
Here's the example:
http://dabblet.com/gist/766f6a238d00bcbb41d4
You can create a filter to add "grout" into the gap :) But it's a hack that works by increasing the opacity of those pixels. It will also roughen up the anti-aliasing of our other edges. Also if you're using opacity in these gradients then you will need to adjust those tableValues below to the range that you want (aka if your fill is 0.4 opacity, then your tableValues would look like "0 .1 .4 .4 .4"). If you have variable opacity in your gradients, then you can play around with other types of component transfers that will preserve the opacity gradients better.
<svg id="background-svg" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 150 75" >
<defs>
<linearGradient y2="1" x2="1" y1="0" x1="0" id="triangleGradient">
<stop stop-color="black" offset="0"/>
<stop stop-color="blue" offset="1"/>
</linearGradient>
<filter id="mynameisgrout">
<feComponentTransfer>
<feFuncA type="table" tableValues="0 .25 1 1 1"/>
</feComponentTransfer>
</filter>
</defs>
<g filter="url(#mynameisgrout)">
<polygon points="0,75 100,75 50,0" fill="url(#triangleGradient)"></polygon>
<polygon points="50,0 150,0 100,75" fill="url(#triangleGradient)"></polygon>
</g>
</svg>
The SVG shape rendering option allows to configure what's important for rendering.
In this case shape-rendering="crispEdges" solves the problem.
See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering
Related
I have the following SVG document:
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="dropShadow">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round">
<path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1"></path>
</g>
</svg>
In Firefox, when I open the SVG document, it simply shows a very thin (not 5 wide) vertical line. In Chrome, it doesn't show anything (nor does it in codepen, here: https://codepen.io/jwir3/pen/BJBqEK ).
I'm not quite sure what I'm doing incorrectly here, but it has something to do with the filter, because, if I remove the filter: url(#dropShadow) from the path definition, the line shows up as expected.
You can't use objectBoundingBox units if your shape has no height or width.
Keyword objectBoundingBox should not be used when the geometry of the applicable element has no width or no height, such as the case of a horizontal or vertical line, even when the line has actual thickness when viewed due to having a non-zero stroke width since stroke width is ignored for bounding box calculations. When the geometry of the applicable element has no width or height and objectBoundingBox is specified, then the given effect (e.g., a gradient or a filter) will be ignored.
The default for filterUnits is objectBoundingBox units so you need to change that to userSpaceOnUse i.e.
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg">
<title>Line Drop Shadow</title>
<description>A red line with 5px width thickness and round caps, having a drop-shadow. This highlights the regression documented in PURP-1017.</description>
<defs>
<filter id="dropShadow" filterUnits="userSpaceOnUse">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round">
<path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1"></path>
</g>
</svg>
When processing filters, different browsers process in different stroke.
Chrome considers stroke as a value with a zero pixel, so it does not include it in the filter region.
Therefore, to make the result look the same in different browsers, it is better to replace path with stroke-width ="5", a rectangle with a width of 5px withoutstroke (stroke="none")
In addition, the default values for the filter area are: x =" - 10% "" y = "- 10%" `` width = "120%" `` height = "120%"- large blur sizes are usually truncated .
By default, filterUnits = "objectBoundingBox" and therefore the values are specified in percentages.
To make it easier to calculate the size of the filter region action, specify the value offilterUnits = "userSpaceOnUse" and then you can specify all dimensions for thefilter region` in pixels.
<svg preserveAspectRatio="xMinYMin meet" width="100%" height="100%" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg" >
<defs>
<filter id="dropShadow" filterUnits = "userSpaceOnUse" x="4" y="0" width="12" height="472">
<feDropShadow dx="6" dy="4" stdDeviation="3"></feDropShadow>
</filter>
</defs>
<g id="Artboard" fill="#FF0000" filter="url(#dropShadow)" >
<!-- <path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1" stroke-width="5" ></path>-->
<rect x="5" y="5" width="5" stroke="none" height="463" />
</g>
</svg>
Swapping to userSpaceOnUse is the correct answer in most circumstances but has the following limitations:
The filter effects region will apply from -10% to 120% of the canvas, rather than the bounding box of the element (using more memory and processing time)
For large dynamic SVGs (such as created by d3) it can be hard to calculate the required filter x/y/width/height to ensure the filter applies to all elements.
An alternate (less elegant) solution is to apply the filter to a <g> and use a hidden node within this to give the group the correct width or height:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="dropShadow" width="20">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" style="filter: url(#dropShadow)">
<circle r="5" cx="0" cy="0" visibility="hidden"></circle>
<path d="M10,10 L10,100" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round"></path>
</g>
</svg>
I've scoured all corners of the web looking for a solution to this but it seems no one else has had this issue.
Here is my current code:
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="mygradient">
<stop offset="0%" stop-color="red"/>
<stop offset="100%" stop-color="yellow"/>
</linearGradient>
</defs>
<path id="myID" d="M 0 78.37136556477951 C 207.875 51.32892778872315 207.875 51.32892778872315 415.75 64.85014667675132 C 623.625 78.37136556477951 623.625 78.37136556477951 831.5 60.035876993171016 C 1039.375 41.70038842156255 1039.375 41.70038842156255 1247.25 64.68862384088634 C 1455.125 87.67685926021014 1455.125 87.67685926021014 1663 60.035876993171016 L 1663 3756 L 0 3756 Z" fill="url(#mygradient)"/>
</svg>
Whenever any browser tries to render this, the <linearGradient> breaks completely. This is how it renders in browsers:
<lineargradient id="mygradient"><stop offset="0%" stop-color="red"></stop offset="0%" stop-color="red"><stop offset="100%" stop-color="yellow"></stop offset="100%"></lineargradient id="mygradient">
As you can see, all spaces in the <linearGradient> become and <linearGradient> becomes <lineargradient>.
Any help with this will be appreciated.
Sorted this one out, pasted my <linearGradient> code snippet into a minifier which seemed to fix the issue. Don't know why but it just did.
I have an SVG image that is being created and enhanced programatically. After creating it, it is drawn on a Canvas. However, the lines do not seem to have the same width, despite having the same value for stroke-width and the attribute shape-rendering set to crispEdges.
The coordinates are calculated in JavaScript (hence the weird numbers). However, some lines seem to be twice as thick as others (see example below). I don't understand why this happens or how I can fix it.
My best guess is that the calculations are not precise enough and the angle is not actually a perfect 45°, resulting in a thicker line. But when I calculate the slope by hand, it's 45°.
Setting shape-rendering to auto theoretically works, but the circumstances require the lines to be not smooth.
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="80" shape-rendering="crispEdges" stroke-linecap="square" stroke="rgb(0,0,0)" stroke-width="1">
<rect id="background" x="0" y="0" width="3201" height="1677" fill="rgb(255,255,255)" stroke-width="0"/>
<line x1="0.5" y1="71.5" x2="71.2106781186546" y2="0.7893218813452"/>
<line x1="71.2106781186546" y1="0.7893218813452" x2="141.9213562373093" y2="71.5"/>
<line x1="141.9213562373093" y1="71.5" x2="212.632034355964" y2="0.7893218813452"/>
<line x1="212.632034355964" y1="0.7893218813452" x2="283.3427124746186" y2="71.5"/>
</svg>
The purpose of the crispEdges attribute is to accentuate the contrast between edges in your picture, not to ensure that strokes are drawn with the same width.
You probably want to use geometricPrecision instead. However, if it's important to use crisp edges for some reason, try drawing your lines with the same gradients and with their start/end points aligned to the pixel grid (ideally, offset by 0.5 pixels).
Here's your SVG, with minor modifications to ensure the stroke width appears consistent:
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="80" shape-rendering="crispEdges" stroke-linecap="square" stroke="rgb(0,0,0)" stroke-width="1">
<rect id="background" x="0" y="0" width="300" height="80" fill="rgb(255,255,255)" stroke-width="0"/>
<line x1="0.5" y1="71.5" x2="71.5" y2="0.5"/>
<line x1="71.5" y1="0.5" x2="142.5" y2="71.5"/>
<line x1="142.5" y1="71.5" x2="213.5" y2="0.5"/>
<line x1="213.5" y1="0.5" x2="284.5" y2="71.5"/>
</svg>
Strange one this.
I've got the following:
<svg aria-hidden="true">
<use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#down"></use>
</svg>
<table>
<tr>
<td>
<svg aria-hidden="true">
<use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#close"></use>
</svg>
</td>
</tr>
</table>
The first icon doesn't show up. However if I change the svg file of the first to:
<svg aria-hidden="true">
<use xlink:href="/assets/icons/standard-sprite/svg/symbols.svg#down"></use>
</svg>
Then both show, is there something I'm missing with SVG from the same file?
It's definitely not a URL thing, because if both <svg> tags use the same URL, then only the second is displayed.
Many Thanks
HOPE THIS HELPS TO ALL NEW PEOPLE
After all this years I stumbled upon the same problem and I solved it...
What happened for me was that the fill attributes of my paths shared the same url(#paint0_linear) and the viewbox of these "fill" where set on the position of the first SVG in the DOM, to solve this just change the name of the fills (and their corresponding id's) of the subsequent SVGS.
English is not my native language so let me show you what I did in code in case I didn't make myself clear:
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="hamb1">
<path id="hambBottom1" fillRule="evenodd" clipRule="evenodd" d="M0 22" fill="url(#paint0_linear)"/>
<path id="hambTop1" d="M13.8599" fill="url(#paint1_linear)"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="-6.5" y1="30" x2="18.9429" y2="34.8415" gradientUnits="userSpaceOnUse">
<stop stopColor="#134F82"/>
<stop offset="1" stopColor="#2BA665"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="-6.50012" y1="30" x2="18.9428" y2="34.8414" gradientUnits="userSpaceOnUse">
<stop stopColor="#134F82"/>
<stop offset="1" stopColor="#2BA665"/>
</linearGradient>
</defs>
</svg>
Check that the fill attribute on the paths NEVER share the same id.
I can't be completely sure since I guess you should explain question properly, But it may be the case that first file path may not be correct (ie with link "/assets/icons/utility-sprite/svg/symbols.svg#down") (ie may be some spell error) so the file is not loading. Try loading file manually with url.
I've got an SVG object that contains a linear gradient embedded directly in a document. It works fine in Chrome and Firefox, but in Safari nothing is rendered. If I create the SVG as a file and embed it using the Object tag, it works fine in Safari. Other shapes and fills work, it's just linear gradient that doesn't work. I guess I could use the object, but I'd prefer to embed the SVG directly.
I've create a demo here (works in Chrome, not Safari): http://jsfiddle.net/sjKbN/
I came across this answer which suggests setting the content type to application/xhtml+xml, but this in itself seems to cause other problems.
Just wondering if anyone had come across any other fixes or ideas to get this working.
Your gradient will work in Safari if you wrap a defs tag around it:
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="300px" height="300px" viewBox="0 0 300 300" enable-background="new 0 0 300 300" xml:space="preserve">
<defs>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="5.6665" y1="149.5" x2="293.333" y2="149.5">
<stop offset="0" style="stop-color:#FFF33B"/>
<stop offset="0.0595" style="stop-color:#FFE029"/>
<stop offset="0.1303" style="stop-color:#FFD218"/>
<stop offset="0.2032" style="stop-color:#FEC90F"/>
<stop offset="0.2809" style="stop-color:#FDC70C"/>
<stop offset="0.6685" style="stop-color:#F3903F"/>
<stop offset="0.8876" style="stop-color:#ED683C"/>
<stop offset="1" style="stop-color:#E93E3A"/>
</linearGradient>
</defs>
<rect x="5.667" y="5.333" fill="url(#SVGID_1_)" width="287.667" height="288.333"/>
</svg>
It seems that wrapping your references in defs is encouraged but not obligatory according to spec. So this is a bug in Safari.
About Alpha : It seems that Safari (7 at this moment) does not cover SVG color alpha channel, use stop opacity attribute. it works fine!
<stop stop-color="rgba(x,y,z,0.5)"> //safari does not work
<stop stop-color="rgb(x,y,z)" stop-opacity="0.5"> //ok
The accepted answer was not the solution for me.
My problem was the presence of a <base href="/" /> tag in my index file. Simply removing it solved the problem for me.
If you cannot remove it, probably some workaround already exist: found this gist but I did not tested it.
Update
Simply removing the href broke the child routing of my angular app, the proper workaround is to prepend to the linearGradient id with the page relative location. I wrapped the logic in a method like this:
get svgFill(): string {
return `url(${this.location.path()}#${this.gradientId}) white`;
}
The answer is simple, all id's (not only <linear gradient>) need to be UNIQUE for all SVG files.
I had some troubles too making an inline SVG with a linear gradient work. The designer had put a - in the id of the <linearGradient. The solution was as simple as removing it.
<linearGradient id="linear-gradient">
...
<path fill="url(#linear-gradient)" d="..."/>
with
<linearGradient id="lineargradient">
...
<path fill="url(#lineargradient)" d="..."/>