Responsive SVG sprites - svg

I am building an icon system for a site and moving away from png and font-icon sprites to SVG.
I need it to work across all major browsers (IE9+) and ideally want to use fragment identifiers or, as a second best alternative, inline. I've done extensive research, and there isn't a great deal out there, and from what I have read SVG sprites aren't particularly responsive.
For fragment identifiers, using img tag, I have to set the width/height to the same size as the viewbox. Increasing the dimensions on the img tag ends up revealing some of the next sprite. I'd like to be able to use percentage values so the sprite fragment fills the parent container.
I just want confirmation that this isn't possible, I can't find anything that suggests I am wrong to think this.

This is very much possible and is relatively easy to accomplish. SVG's are vector graphics and therefore, if done correctly, will be the most responsive graphics on your website.
Set your SVG file up as expected but put each sprite into a <g> tag with its own identifier.
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g class="sprite" id="circle">
<ellipse cy="50" cx="50" ry="45" rx="45" stroke-width="5" stroke="#00ff00" fill="#00ff00" fill-opacity="0.75" />
</g>
<g class="sprite" id="square">
<rect y="5" x="5" width="90" height="90" stroke-width="5" stroke="#ff0000" fill="#ff0000" fill-opacity="0.75" />
</g>
<g class="sprite" id="triangle">
<path d="M20,7 L92,50 L6,93 z" stroke-width="5" stroke="#0000ff" fill="#0000ff" fill-opacity="0.75" />
</g>
</svg>
Add some CSS to say only the rargeted one needs to be displayed
<defs>
<style><![CDATA[
.sprite { display: none; }
.sprite:target { display: inline; }
]]></style>
</defs>
Then you can just call these out whenever required using an img tag or background element etc.
You can find the fully explained writeup here:
How to Use SVG Image Sprites

Related

convert or mogrify to rotate an svg file on command line : "delegate failed" error

I want to rotate an image in an svg file by x degrees in the very straightforward way, on the command line (not via a gui program), such that if a picture were on your kitchen table the picture would be simply turned around to face to the side, or where ever - i.e. real straightforward (one hopes).
[ADDED INFO]: the svg file is from gnuplot multiplot mode, in particular - but of course gnuplot has many terminals. I say this because multiplot appears unable to rotate the computed image by 90 (in the "do what I mean" sense - even though the statement in plain English does not make sense ); however, multiplot can do that to rgb files - see the famous tutorial page with Tux images.
If an svg file is given to convert to rotate by 90 degrees (for instance) :
convert -rotate 90 test_start.svg test_plus_90.svg
stderr (I guess) is reported:
convert-im6.q16: delegate failed `'potrace' --svg --output '%o' '%i'' # error/delegate.c/InvokeDelegate/1966.
no output is produced and the original file is unchanged. if mogrify is used instead, the same error plus this one is issued [ UPDATE: this has to do with the output file, but I'll leave this as it is trivial]:
mogrify-im6.q16: unable to open image `test_plus_90.svg': No such file or directory # error/blob.c/OpenBlob/2924.
the original file is then appended with a tilde, as test_start.svg~, but appears unchanged.
png and jpg files will process this way fine. It appears that some resources need to be installed, as the "Delegates" for convert do not include svg - or this is the incorrect way to process svg files, I am wrong, etc.
version and system information:
Ubuntu 22, synaptic used to install everything.
ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25
Delegates do NOT include svg, so this appears to be what I need to understand.
gimp version 2.10.30
tl;dr You are better off first rendering your SVG image as a raster image, with the final dimensions for display, and only then flipping it around. Or, depending on your use case, quoting the image in a reference and rotating the result.
Flipping a SVG image by 90° is not a straightforward task, if you want the result to be still in SVG format. At least if you tried to write a tool that covered all the necessary bases for it not to produce seriously botched pictures. Let me give you an example:
<svg xmlns="http://www.w3.org/2000/svg" width="150vh" height="100vh" viewBox="0 0 400 200">
<rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
<circle r="10" cx="5%" cy="50%" fill="#00f" />
<circle r="10" cx="50%" cy="50%" fill="#00f" />
<circle r="10" cx="95%" cy="50%" fill="#00f" />
</svg>
How would you change that file to flip it around? You would exchange the width and height attributes on the root element, equally the corresponding values in the viewBox attribute. (If there was a preserveAspectRatio attribute, it would also have to be converted, but I'll leave that out.) Then, the content inside has also to be rotated by 90°. Wrap all content in a <g> element with an appropriate transform attribute:
<svg xmlns="http://www.w3.org/2000/svg" width="100vh" height="150vh" viewBox="0 0 200 400">
<g transform="rotate(90, 100,100)">
<rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
<circle r="10" cx="5%" cy="50%" fill="#00f" />
<circle r="10" cx="50%" cy="50%" fill="#00f" />
<circle r="10" cx="95%" cy="50%" fill="#00f" />
</g>
</svg>
Oops. What happened? The cx/cy values of the circles are percentages of width and height of the viewBox, which are already exchanged. But then the rotation is applied, and axis directions for the purpose of positioning are at odds.
This is obviously not the way to go about it. A conversion tool would have to sift through the complete code looking for these sort of pitfalls and correcting attributes. And let me assure you, I can think of a lot of tricky constructs that would trip implementers.
Here is a second attempt. <svg> elements can be nested, and they encapsulate their viewport completely. Instead of changing content, wrap the root element in another one and flip the now inner <svg> as a whole.
But there is a pitfall to avoid: A construct like
<svg ...>
<svg transform="rotate(90)" ...>
is only part of the SVG 2 specification. While browsers mostly have implemented that, other renderers may still disregard the transform attribute on the inner <svg>, as prescribed by the SVG 1.1 spec.
Thus you need to introduce an extra <g transform="..."> element:
<svg xmlns="http://www.w3.org/2000/svg" width="100vh" height="150vh" viewBox="0 0 200 400">
<g transform="rotate(90 100,100)">
<svg width="400" height="200" viewBox="0 0 400 200">
<rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
<circle r="10" cx="5%" cy="50%" fill="#00f" />
<circle r="10" cx="50%" cy="50%" fill="#00f" />
<circle r="10" cx="95%" cy="50%" fill="#00f" />
</svg>
</g>
</svg>
Well, it seemed to work. Note that the width/height attributes on the inner <svg> needed to be changed. (I assume there is a valid strategy even if one or two of the sizing attributes are missing in the original file, but haven't tested them all.) But imagine a tool flipping the image multiple times. Would it wrap the previous version each time with new outer elements? Would it try to analyze the structure and fold possible multiple applications of flipping into one? Most developers would probably cringe instead of writing such a tool.
This is not to say that one like this does not exist, But I don't know about it. It is so much easier to change your workflow: stall the flipping to the moment when you render the SVG image to a raster format, and do the flipping afterward and on-the-fly, where it is an easy task.
If you are using the SVG as an image in other content, it is equally easy to combine rotating with referencing. For example in HTML/CSS:
<img src="myImage.svg"
style="width:...;height:...;transform:rotate(90deg);transform-origin:...">

SVG <defs> not included from external target of <use> [duplicate]

I'm trying to hack together a sprite sheet from a set of icons. I know almost nothing about SVG. I can get the simple icons to work, but an icon with a clip path isn't displaying properly. From what I can tell it seems like it's not using the clip path.
The sprite works in jsfilddle and it works if I just load the svg on it's own and include a < use > statement in the SVG. But if I have a separate < use > it doesn't work.
All my testing has been done in Chrome (50.0.2661.94)
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<clipPath id="folder-clip-0">
<path d="..." />
</clipPath>
<symbol id="folder" viewBox="0 0 32 32">
<g class="container" data-width="32" data-height="27" transform="translate(0 2)">
<path d="..." class="..." />
<path class="..." d="..." />
<path clip-path="url(#folder-clip-0)" d="..." class="..." />
</g>
</symbol>
</defs>
</svg>
I'm using it like so:
<svg>
<use
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/img/path/sprite.svg#folder">
</use>
</svg>
When I use the separate statement it looks like this:
But it should look like this:
The color difference is not relevant, it's just the background when the image was taken.
Edit:
I just discovered that if I dump the whole sprite sheet into the page HTML and reference it locally instead of an external file it works. So I don't know what's wrong with my external reference.
e.g.
<svg>
<use xlinkHref={"/img/path/not/work/sprite.svg#folder"}></use>
</svg>
vs.
<svg>
<symbol id="folder"></symbol>
</svg>
<svg>
<use xlinkHref={"#folder"}></use>
</svg>
This works for me as a fallback, but I'd rather have an external SVG file instead of embedding it in my HTML.
Edit 2:
If the SVG sprite sheet is embeded in the HTML directly using the external link shows the icon correctly.
This seems to be a browser support issue. Using the external reference works as expected in Firefox. Chrome doesn't handle clip paths and some other functions in external references. There's an outstanding bug report filed. Safari also doesn't support it.
Related StackOverflow ticket: Why can't I reference an SVG linear gradient defined in an external file (paint server)?
Open bugs:
https://code.google.com/p/chromium/issues/detail?id=109212
https://bugs.webkit.org/show_bug.cgi?id=105904

Clip path not displaying properly in SVG sprite when using "use"

I'm trying to hack together a sprite sheet from a set of icons. I know almost nothing about SVG. I can get the simple icons to work, but an icon with a clip path isn't displaying properly. From what I can tell it seems like it's not using the clip path.
The sprite works in jsfilddle and it works if I just load the svg on it's own and include a < use > statement in the SVG. But if I have a separate < use > it doesn't work.
All my testing has been done in Chrome (50.0.2661.94)
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<clipPath id="folder-clip-0">
<path d="..." />
</clipPath>
<symbol id="folder" viewBox="0 0 32 32">
<g class="container" data-width="32" data-height="27" transform="translate(0 2)">
<path d="..." class="..." />
<path class="..." d="..." />
<path clip-path="url(#folder-clip-0)" d="..." class="..." />
</g>
</symbol>
</defs>
</svg>
I'm using it like so:
<svg>
<use
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/img/path/sprite.svg#folder">
</use>
</svg>
When I use the separate statement it looks like this:
But it should look like this:
The color difference is not relevant, it's just the background when the image was taken.
Edit:
I just discovered that if I dump the whole sprite sheet into the page HTML and reference it locally instead of an external file it works. So I don't know what's wrong with my external reference.
e.g.
<svg>
<use xlinkHref={"/img/path/not/work/sprite.svg#folder"}></use>
</svg>
vs.
<svg>
<symbol id="folder"></symbol>
</svg>
<svg>
<use xlinkHref={"#folder"}></use>
</svg>
This works for me as a fallback, but I'd rather have an external SVG file instead of embedding it in my HTML.
Edit 2:
If the SVG sprite sheet is embeded in the HTML directly using the external link shows the icon correctly.
This seems to be a browser support issue. Using the external reference works as expected in Firefox. Chrome doesn't handle clip paths and some other functions in external references. There's an outstanding bug report filed. Safari also doesn't support it.
Related StackOverflow ticket: Why can't I reference an SVG linear gradient defined in an external file (paint server)?
Open bugs:
https://code.google.com/p/chromium/issues/detail?id=109212
https://bugs.webkit.org/show_bug.cgi?id=105904

Can you combine multiple svgs with different viewBoxes and customize each in Polymer?

I have multiple svgs that I'd like to use in my application and was hoping to put them in a single custom-svg element to reference individually by id, however, the viewBoxes are different. One svg is defined as
<iron-iconset-svg name="club-icon" size="512">
<svg>
<defs>
<g id="club-icon">
<path d="bunch of numbers"></path>
</g>
</defs>
</svg>
</iron-iconset-svg>
The other svg is defined as
<iron-iconset-svg name="club-icon" size="300">
<svg>
<defs>
<g id="book-icon">
<path d="bunch of numbers"></path>
</g>
</defs>
</svg>
</iron-iconset-svg>
Is there a way for each custom icon to define its own viewBox, or must every svg defined within a single iconset share the same properties. For now I have multiple custom element html files, but each custom element is an http request (which I'm trying to minimize).
It can be done using symbols.
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="beaker" viewBox="214.7 0 182.6 792">
<!-- <path>s and whatever other shapes in here -->
</symbol>
<symbol id="shape-icon-2" viewBox="0 26 100 48">
<!-- <path>s and whatever other shapes in here -->
</symbol>
</svg>
See this article on CSS tricks for a further explanation.
https://css-tricks.com/svg-symbol-good-choice-icons/
you might try a transform scale on you g element?
<g id="book-icon" transform="scale(1.71)">
1.71 = 512/300
or if you are using gulp/grunt, you could resize the SVGs to be the same using svg-scaler or the like

Enforce relative links to defs in SVG

I'm trying to draw multiple SVGs on a single page and the ids in the defs section of each SVG are clashing. I'd like each one to refer only the mask in their own defs. Currently they all use the mask that has a matching id on the first svg on the page. No svg knows about the others so they would have to rely on a random number generator to pick ids that (hopefully) are different.
Is that possible or do they need unique ids if the SVGs are loaded into the same webpage. They are created on the fly by d3.
<svg>
<defs>
<mask id="temperatureMask">
<rect width="100%" height="100%" fill="#ffffff">
</mask>
</defs>
<rect mask="#temperaureMask">…etc
</svg>
<svg>
<defs>
<mask id="temperatureMask">
<rect width="100%" height="50%" fill="#dddddd">
</mask>
</defs>
<rect mask="#temperaureMask">…etc
</svg>
The ids on a page must be unique. That includes any inline SVGs. You will need to alter your d3 script like you suggest to add a unique number etc to the ids.

Resources