如何在SVG精灵符号内引用内部渐变定义?

29

摘要: 一个SVG精灵包含五个图标<symbol>块,其中一个引用其自身的渐变定义ID。它无法再找到该渐变并正确渲染。

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


我正在将一些图形切换到SVG格式,并且因为它们是图标(在这种情况下是社交网络配置文件),所以我想将它们保存在精灵中(就像之前使用PNG时一样)。

我遵循了CSS-tricks.com上有关SVG精灵的指南(以及建议使用<symbol>而不是<g>的后续内容)。

现在我有一个SVG精灵文件,名为social-sprite.svg,您可以在此处完整查看。

这是一个完整的<svg>块,其中包含五个不同的<symbol>块,每个块都有一个id和一个viewBox属性。在每种情况下,我通过在Adobe Illustrator中准备官方图标并保留已处理代码的相关部分来获取每个符号的SVG代码。

该.svg文件通过PHP包含,即在<body>标签打开时(这就是为什么其中的主要<svg>容器带有style="display:none;")以便从HTML中使用对每个符号的引用。

四个图标完美工作,唯一出现问题的是YouTube图标,因为它使用了内部定义的渐变。以下是SVG代码中的YouTube部分:

<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>

这在HTML中被称为:

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

前两个路径没问题,问题在于在这个新的组合精灵SVG文件中,每个图标都被分离为<symbol>,而"菱形" <path>无法找到对<linearGradient>#SVGID_1_引用。

在Firefox中,这会导致菱形显示为白色(我猜测,也许根本没有显示-还没有仔细研究过):

firefox fills as white

而Chrome则以黑色渲染它:

chrome fills as black

很明显,两者都不可接受。目前我能做的唯一一件事就是在路径上删除fill="url(#SVGID_1_)",只需填充适合YouTube标志的红色单色即可。然而,即使不考虑在此方式下篡改YouTube标志将不被他们的品牌指南所接受,这也不是一个正确的解决方案。

我尝试过的事情(但没有成功):

  • 移除包围渐变和路径的两个<g>包装器,因此整个符号只是<path>-<path>-<linearGradient>-<path>
  • 将渐变定义包装在<defs>容器中
  • 将其包装在<defs>中,并将其移至SVG文件的顶部,即YouTube特定的<symbol>边界之外
  • 更改ID名称(你永远不知道!)
  • 使用百分比而不是像素值重新定义渐变

那么如何使已经内部的<symbol>引用同样内部的<linearGradient>定义呢?


编辑:事实证明,当整个<svg>块标记为style="display: none;"时,渐变会失败。如果删除此样式,则渐变会正确呈现。但是要提醒的是,添加此样式是为了在导入SVG精灵时,它不会立即呈现在页面上,只是允许您根据需要对id-defined符号进行引用。

visibility: hiddenopacity: 0都可以使渐变正确呈现,但很明显它们并不提供适当的解决方案,因为它们仍然标记了SVG如果可见时将占据的空间。

在发现所有这些问题后,我很确定只需将完全可见的<svg>添加到隐藏的容器<div> 内部即可。但是,即使这样也会导致渐变无法呈现。我离解决问题还有很远的路要走。


你可以尝试将其包装在<defs>标签中? - Michael Mullany
1
@MichaelMullany 那行不通。 - Robert Longson
2个回答

38

首先请注意我问题的修改——在此之后,我发现使用 display: none 隐藏 SVG 符号直到需要它们是问题所在。

我一直在摆弄并最终采用了这个“答案”,虽然远非完美,但对于任何类似情况仍应该是可靠的。

你需要做的就是将整个 <svg> 代码包装在 <div> 容器中,这个容器必须显示但不会影响布局,所以我只需通过超级过度的 CSS 来完成这个操作,例如:

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

这样就很好用了。查看最终的演示: http://jsfiddle.net/Qtq24/5/

如果有更好的解决方案,我很乐意听取建议,因为这感觉有点 hacky,但我认为比使用 display: none; 更好。


2
我真的很苦恼,感谢你抽出时间来回答我的问题! - nickspiel
SVG存在一些奇怪的问题,如果包含渐变元素的SVG被设置为display:none(或其父级之一),则无论在何处引用该渐变,它都不会显示。我不确定这是否是浏览器的错误还是有意为之。 - Jake Wilson
这实际上是解决问题的正确方法,因为浏览器被告知不要渲染 display:none; 元素。谢谢。 - Eric
2
您,先生,是一个救星。 - Kashif
你不能直接给SVG本身添加这些样式吗? - trysis

7

不要在SVG中使用style="display: none;"。您可以在根<svg>元素上使用visibility:hidden、height/width="0"或<defs>等更好的替代方案。

Firefox早期版本中存在一个有关符号渐变元素的错误,但该错误现已被修复。原始代码按预期工作。

<svg width="30" height="21">
<symbol id="youtube" viewBox="0 0 400 281.641">
  <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 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>

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


对我来说运气不好,Robert - 请参见http://jsfiddle.net/Qtq24/3/ - 在Firefox(白色)和Chrome(黑色)中仍然是以前的相同结果,两者都无法找到尽管它已经移出了<symbol>的渐变。这实际上是我之前尝试过的事情之一,我确定 - 有和没有<defs>容器...搜索继续进行。 - Matt Morrison
对于不使用display: none的建议,我很抱歉有所忽略 - 如果我早点发现这个问题,我就能更快地得出答案了!但正如所描述的那样,这是为了防止SVG精灵在代码加载时立即呈现符号而必需的。 - Matt Morrison

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接