How can I bundle many SVG images inside just one?

2019-08-23 09:59发布

It's not bad if I load an HTML page

img, svg {
    background-color: #eee;
    margin: 20px;
}
<div>
    <img src="circle.svg"/>
    <img src="square.svg"/>
</div>

with just a pair of SVG images

circle.svg

<svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 400 300" width="400" height="300">
    <circle cx="200" cy="150" r="100"
        stroke="red" fill="blue" stroke-width="10" />
</svg>

square.svg

<svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 400 300" width="400" height="300">
    <rect x="100" y="50" width="200" height="200"
        stroke="green" fill="gray" stroke-width="10" />
</svg>

but with over a hundred SVG images, the server gets excessively hit by requests.

One solution is to serve the static files from a dedicated server, but this only dodges the problem. The number of requests remains high.

How can I bundle many SVG images inside just one?

3条回答
混吃等死
2楼-- · 2019-08-23 10:28

The following is a combination of gael's and maqam7's answers, with a bug fix and some details.

First, we combine the two SVGs into one. (We write our own script, use an editor's macros, use one of the web sites that do it, or do it by hand.)

sprite.svg

<svg id="mysprite" xmlns="http://www.w3.org/2000/svg">
    <symbol id="circle"
        viewBox="0 0 400 300"
        width="400" height="300">
        <circle cx="200" cy="150" r="100"
            stroke="red" fill="blue" stroke-width="10" />
    </symbol>

    <symbol id="square"
        viewBox="0 0 400 300"
        width="400" height="300">
        <rect x="100" y="50" width="200" height="200"
            stroke="green" fill="gray" stroke-width="10" />
    </symbol>
</svg>

When we want a circle or a square, we use the xlink:href attribute (deprecated but continue to use it), which will invoke a sub-sprite.

<div class="container">
    <svg>
        <use xlink:href="sprite.svg#circle"></use>
    </svg>
    <svg>
        <use xlink:href="sprite.svg#square"></use>
    </svg>
</div>

There is no need to include the sprite in the body

<img src="sprite.svg"/>

as the sprite is referenced within each svg element.

Hence there is no need to hide the global sprite.

#svg-sprite {
    display: none;
}

Only the sub-parts appear.

One caveat: Chrome loads an img and svg directly, but will refuse to load use/xlink:href unless you run a local server.

Remaining issue(s)

I'm not sure this is optimal. It may be that two requests continue to be sent. It's just that the cache will catch the second as identical. No harm is done. Still, loading once via a hidden svg may be a better approach, if someone can fill in the details.

查看更多
小情绪 Triste *
3楼-- · 2019-08-23 10:42

You could use a SVG sprite generator to create one big file with all images aligned in it.

SVG sprite generator will also generate a CSS file in which each individual SVG will be represented with a specific class.

In you HTML you just have to call each image by its class name.

A basic sprite.css could be :

.svg {
    background-image: url(sprite.svg) no-repeat;
}
.circle {
    background-position: top left;
    height:300px;
    width: 400px;
}
.square{
    background-position: top right;
    height:200px;
    width: 200px;
}

And then in your html file you could just call:

<div>
    <div class="circle"></div>
    <div class="square"></div>
</div>
查看更多
forever°为你锁心
4楼-- · 2019-08-23 10:54

It sounds like you need an SVG sprite. I use this trick all the time. It's great. Just make your svg blocks symbol elements and nest them inside an svg like this:

<svg id="svg-sprite" xmlns="http://www.w3.org/2000/svg">
  <symbol id="svg-circle" viewBox="0 0 400 300" width="400" height="300">
    <circle cx="200" cy="150" r="100" stroke="red" fill="blue" stroke-width="10" />
  </symbol>

  <symbol id="svg-square" viewBox="0 0 400 300" width="400" height="300">
    <rect x="100" y="50" width="200" height="200" stroke="green" fill="gray" stroke-width="10" />
  </symbol>
</svg>

Note that you don't want the xlmns attribute on the individual symbol elements, just the root svg. And the root svg doesn't need a viewBox attribute, since that is encoded in the child symbol elements.

Then you call the symbols elsewhere in the HTML like this via the <use> tag:

<svg>
  <use xlink:href="#svg-circle"></use>
</svg>

Lastly, you need to hide the sprite in CSS:

#svg-sprite {
  display: none;
}

Here's a Fiddle to demonstrate. Good luck!

查看更多
登录 后发表回答