How can I refer to an internal gradient definition inside an SVG sprite symbol?

我的未来我决定 提交于 2019-12-20 08:41:11

问题


SUMMARY: An SVG sprite contains five icon <symbol> blocks, one of which references its own gradient definition by ID. It is no longer able to find this gradient and render properly.

JSFIDDLE: http://jsfiddle.net/Qtq24/1/


I am switching some graphics to SVG, and being that they are icons (in this case for social networking profiles) I'd like to keep them in a sprite (as I had with PNG before).

I've followed this guide to SVG sprites on CSS-tricks.com (along with this follow-up which advises using <symbol> instead of <g>).

I now have an SVG sprite file, social-sprite.svg, which you can view in full here.

This is one complete <svg> block containing five different <symbol> blocks, each with an id and with a viewBox attribute. In each case I got the SVG code for each symbol by preparing official icons in Adobe Illustrator and retaining the relevant parts of the processed code.

The .svg file is included via PHP as soon as the <body> tag opens (and this is why the main <svg> container inside it is marked with style="display: none;") so that the references to each symbol work from the HTML.

Four icons work perfectly, and the only one I am having trouble with is the YouTube icon, because it uses an internally-defined gradient. Here is the YouTube part of the SVG code:

<symbol id="youtube" viewBox="0 0 400 281.641">
    <path id="Triangle" fill="#FFFFFF" d="M159.845,191.73l106.152-54.999L159.845,81.348V191.73z"/>
    <path id="The_Sharpness" opacity="0.12" fill-rule="evenodd" clip-rule="evenodd" fill="#420000" d="M159.845,81.348l93.091,62.162
    l13.061-6.778L159.845,81.348z"/>
    <g id="Lozenge">
        <g>
            <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="200.4204" y1="2.6162" x2="200.4204" y2="278.9292">
                <stop  offset="0" style="stop-color:#E52D27"/>
                <stop  offset="1" style="stop-color:#BF171D"/>
            </linearGradient>
            <path fill="url(#SVGID_1_)" d="M392.928,62.226c0,0-3.839-27.073-15.617-38.995C362.371,7.583,345.626,7.506,337.947,6.59
            c-54.975-3.974-137.441-3.974-137.441-3.974h-0.171c0,0-82.464,0-137.44,3.974c-7.68,0.916-24.419,0.993-39.364,16.641
            C11.753,35.153,7.92,62.226,7.92,62.226s-3.929,31.792-3.929,63.583v29.805c0,31.791,3.929,63.582,3.929,63.582
            s3.833,27.073,15.611,38.995c14.945,15.646,34.575,15.152,43.318,16.792c31.43,3.015,133.571,3.946,133.571,3.946
            s82.552-0.124,137.526-4.099c7.679-0.915,24.424-0.993,39.363-16.64c11.778-11.922,15.617-38.995,15.617-38.995
            s3.923-31.791,3.923-63.582v-29.805C396.851,94.017,392.928,62.226,392.928,62.226z M159.863,191.73l-0.018-110.383
            l106.152,55.384L159.863,191.73z"/>
        </g>
    </g>
</symbol>

And this is called in the HTML with:

<svg width="30" height="21">
    <use xlink:href="#youtube" src="fallback.png" width="30" height="21" />
</svg>

The opening two paths work fine, the problem is that in this new combined sprite SVG file, with each icon separated as a <symbol>, the "Lozenge" <path> is unable to find the #SVGID_1_ reference to the <linearGradient>.

In Firefox this causes the lozenge to display as white (I assume, perhaps it is not displaying at all - not really looked into it):

whilst Chrome renders it in black:

Obviously neither is acceptable. The only thing I can do at the moment is remove fill="url(#SVGID_1_)" on the path and just fill with a flat colour red appropriate to the YouTube logo. This is not a proper solution though, even discounting the fact that bastardising the YouTube logo in this way would not be accepted under their brand guidelines.

Things I've tried (and had no luck with):

  • removing the two <g> wrappers that surround the gradient and the path, so the whole of the symbol is just <path>-<path>-<linearGradient>-<path>
  • wrapping the gradient definition inside a <defs> container
  • wrapping it in a <defs> and also moving it to the top of the SVG file, i.e. outside the bounds of the YouTube-specific <symbol>
  • changing ID name (you never know!)
  • redefining the gradient with percentages rather than pixel values

So how do I get an already-internal <symbol> to reference an also-internal <linearGradient> definition?


EDIT: It turns out the gradient fails when the whole <svg> block is marked with style="display: none;". If this style is removed, the gradient renders properly. But as a reminder, this styling is added so that when you import the SVG sprite it is not rendered instantly on the page, and just allows you to make references to the id-defined symbols as required.

visibility: hidden or opacity: 0 both allow the gradient to render properly, obviously they don't offer proper solutions though as they still demarcate the space that the SVG would have taken up if visible.

After discovering all this, I was pretty sure it would be no problem to have the fully visible <svg> with no stylings added INSIDE a container <div> which is hidden. However, even this causes the gradient not to render. I'm no closer to solving the issue.


回答1:


Firstly please note the edit to my question - whereupon I discover that the use of display: none to hide the SVG symbols until we need them was the problem.

I kept fiddling and settled upon this "answer", which is far from perfect, but should still be reliable for any such situation.

All you need to do is wrap the entire <svg> code in a <div> container which must be displayed but will never affect layout, so I've just done this via mega overkill CSS such as:

height: 0; width: 0; position: absolute; visibility: hidden;

And this works great. See the final fiddle: http://jsfiddle.net/Qtq24/5/

If anyone has a better solution, I'd love to hear it as this feels like a bit of a hacky way of doing it but I guess no more hacky than having to use display: none; anyway.




回答2:


You've two issues:

Don't use style="display: none;" in SVG. You have it on the root <svg> element. Either visibility:hidden, height/width="0" or <defs> are better alternatives.

There's a bug in Firefox that gradient elements don't work if they are inside symbols. The workaround is pretty simple though, just move your linearGradient outside the symbol so it looks like this...

<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="200.4204" y1="2.6162" x2="200.4204" y2="278.9292">
    <stop  offset="0" style="stop-color:#E52D27"/>
    <stop  offset="1" style="stop-color:#BF171D"/>
</linearGradient>
<symbol id="youtube" viewBox="0 0 400 281.641">
    <path id="Triangle" fill="#FFFFFF" d="M159.845,191.73l106.152-54.999L159.845,81.348V191.73z"/>
    <path id="The_Sharpness" opacity="0.12" fill-rule="evenodd" clip-rule="evenodd" fill="#420000" d="M159.845,81.348l93.091,62.162
    l13.061-6.778L159.845,81.348z"/>
    <g id="Lozenge">
        <g>
            <path fill="url(#SVGID_1_)" d="M392.928,62.226c0,0-3.839-27.073-15.617-38.995C362.371,7.583,345.626,7.506,337.947,6.59
            c-54.975-3.974-137.441-3.974-137.441-3.974h-0.171c0,0-82.464,0-137.44,3.974c-7.68,0.916-24.419,0.993-39.364,16.641
            C11.753,35.153,7.92,62.226,7.92,62.226s-3.929,31.792-3.929,63.583v29.805c0,31.791,3.929,63.582,3.929,63.582
            s3.833,27.073,15.611,38.995c14.945,15.646,34.575,15.152,43.318,16.792c31.43,3.015,133.571,3.946,133.571,3.946
            s82.552-0.124,137.526-4.099c7.679-0.915,24.424-0.993,39.363-16.64c11.778-11.922,15.617-38.995,15.617-38.995
            s3.923-31.791,3.923-63.582v-29.805C396.851,94.017,392.928,62.226,392.928,62.226z M159.863,191.73l-0.018-110.383
            l106.152,55.384L159.863,191.73z"/>
        </g>
    </g>
</symbol>


来源:https://stackoverflow.com/questions/24806913/how-can-i-refer-to-an-internal-gradient-definition-inside-an-svg-sprite-symbol

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!