w0rm / gulp-svgstore

Combine svg files into one with symbol elements
https://www.npmjs.com/package/gulp-svgstore
646 stars 33 forks source link

SVG viewbox metadata #15

Closed necolas closed 9 years ago

necolas commented 10 years ago

In order to use the output correctly for SVG's that have variable widths, you need to include the viewBox in the HTML:

<svg viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
  <use xlink:href="#icon-close"></use>
</svg>

What do you think about this plugin writing icons.json or similar, with name: viewbox pairs, so that downstream tools or a server can have access to the correct viewBox value for each icon.

necolas commented 10 years ago

Or perhaps provide another option like transformSvg but to operate on a per-icon basis, rather than the output svg. If the name was more generic then it could be used for purposes other than the specific metadata extraction I need.

w0rm commented 10 years ago

Can you provide an example of what you want to achieve? Each symbol tag in combined svg has viewBox so you don't need to specify it for svg element that uses symbol.

On 18 Oct 2014, at 00:38, Nicolas Gallagher notifications@github.com wrote:

In order to use the output correctly for SVG's that have variable widths, you need to include the viewBox in the HTML:

What do you think about this plugin writing icons.json or similar, with name: viewbox pairs, so that downstream tools or a server can have access to the correct viewBox value for each icon.

— Reply to this email directly or view it on GitHub.

w0rm commented 10 years ago

transformSvg is hacky indeed, I introduced it because I didn't want to bloat plugin with lots of options. Ideally, transformSvg should be another gulp plugin, so we would be able to use it for sources and for the result svg.

I don't have a full internet connection, but perhaps there is already some kind of gulp plugin that lets you manipulate xml files.

On 18 Oct 2014, at 01:04, Nicolas Gallagher notifications@github.com wrote:

Or perhaps provide another option like transformSvg to operate on a per-icon basis, rather than the output svg. If the name was more generic then it could be used for purposes other than the specific metadata extraction I need.

— Reply to this email directly or view it on GitHub.

necolas commented 10 years ago

Each symbol tag in combined svg has viewBox so you don't need to specify it for svg element that uses symbol.

That isn't correct. The css-tricks article is wrong about that. If your icons have variable width, then you need to include the viewbox on each reference to the combined svg if you want the icon to scale proportionally.

I ended up writing a script rather than using gulp and this plugin. But there is an inherit limitation to this technique if you don't also have knowledge of the viewbox values.

w0rm commented 10 years ago

Interesting! Would be cool if you could craft an example of the icon that scales proportionally. I was only able to do it using percentage padding trick on the container to set the ratio and then using absolute position for svg inside.

On 18 Oct 2014, at 20:25, Nicolas Gallagher notifications@github.com wrote:

Each symbol tag in combined svg has viewBox so you don't need to specify it for svg element that uses symbol.

That isn't correct. The css-tricks article is wrong about that. If your icons have variable width, then you need to include the viewbox on each reference to the combined svg if you want the icon to scale proportionally.

— Reply to this email directly or view it on GitHub.

necolas commented 10 years ago

What you need is:

<!-- 'viewBox' preserves the correct width/height ratio -->
<svg class="icon" title="{{name}}" viewBox="{{viewBox}}">
  <!-- 'fill="currentcolor"' allows for simpler color styling -->
  <use fill="currentcolor" xlink:href="#icon-{{name}}"></use>
</svg>

Then you can use CSS to control size and color:

.Icon {
  /* setting the color doesn't require use of 'fill' */
  color: green;
  /* proportional scaling only requires height to be set */
  height: 1em;
}

Setting the correct viewBox for each svg isn't a problem if you're using a templating system / ui-component architecture. But it's a pain if you're building your markup by hand. It seems to be the only way to use properly SVG icons that don't all use a fixed width/height ratio.

w0rm commented 10 years ago

@necolas thanks! I have to check if this works in all of the browsers. Right now it is possible to solve your problem with current api, inside transformSvg function.

var jsonData = $svg.children('symbol').map(function () {
  return {
    name: $(this).attr('id'),
    viewBox: $(this).attr('viewBox')
  }
}).get()

Will produce jsonData similar to this:

[ { name: 'icon-circle', viewBox: '0 0 40 40' },
  { name: 'icon-square', viewBox: undefined } ]

I'm not going to close this issue, and will think of a better solution to this problem.

Frizi commented 10 years ago

I have done something similar lately, and ended up writing separate plugin for it (unfortunately private). The only missing thing was the ability to push new file down the stream from transform function.

If the stream object (or its push function) could be passed into the transform function, it would allow to generate files on the fly and push them down the stream along with svg.

For now as a workaround you can push it down the other, previously prepared stream and join then at the end with event-stream's es.merge().

w0rm commented 10 years ago

@Frizi its not hard to implement, just change transform call to transformSvg.call(this, $combinedSvg, done) and you'll be able to call this.push from within transformation function. But I'm not sure if it is nice as an api.

w0rm commented 9 years ago

@necolas @Frizi I added an example of how to extract viewboxes from combined svgs: https://github.com/w0rm/gulp-svgstore#extracting-metadata-from-combined-svg

This is now possible because gulp-svgstore caches cheerio in gulp file object.

If you need to extract from svg sources, then you may do the same with gulp-cheerio, pipe it to through2, collect data in transform function and write result json in flush function.

necolas commented 9 years ago

Cool. We wrote our own script to accommodate various requirements, but this will be useful for normal projects. Thanks!

w0rm commented 9 years ago

@necolas transformSvg wasn't a good idea in the first place, gulp-cheerio combination seems to be much better. It doesn't degrade performance, because cached file.cheerio is used instead of parsing file multiple times.