如何使用外部CSS样式来美化SVG?

149

我有几个SVG图形,我希望通过我的外部样式表修改它们的颜色 - 而不是直接在每个SVG文件中进行修改。我没有将图形内联,而是将它们存储在我的图像文件夹中并指向它们。

我以这种方式实现它们,以便工具提示可以正常工作,并且我还将每个图形包装在<a>标签中以允许链接。

<a href='http://youtube.com/...' target='_blank'><img class='socIcon' src='images/socYouTube.svg' title='View my videos on YouTube' alt='YouTube' /></a>

这里是SVG图形的代码:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
<g>
    <path d="M28.44......./>
</g>
</svg>

我将以下内容放在我的外部CSS文件(main.css)中:

.socIcon g {fill:red;}

然而它对图形没有影响。我也尝试了.socIcon g path {}.socIcon path {}

似乎出了点问题,也许是我的实现不允许外部CSS修改,或者我错过了什么步骤?非常感谢你的帮助!我只需要通过我的外部样式表修改SVG图形的颜色能力,但我不能失去工具提示和链接功能(虽然我可能可以没有工具提示)。


请参见https://dev59.com/dGjWa4cB1Zd3GeqPu-Tv,了解与SVG嵌入式样式表相关的安全限制。 - Erik Dahlström
1
尝试使用 svg { fill:red; } 或者给你的路径命名一个类名,例如 <path class="socIcon" d="M28.44 ..... /> 这样应该就可以了。 - Dwza
1
你可以在<svg>元素内部使用以下任意一种方式来引用样式文件styles.css: <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="styles.css"/> 或者 <style>@import url(styles.css);</style> - BarryCap
也许最容易做的是查看 MDN,它有一些很棒的解决方案:https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_and_CSS - user3204365
17个回答

119

只有在 SVG 文件内联包含在 HTML 中时,您的 main.css 文件才会对 SVG 内容产生影响:

https://developer.mozilla.org/zh-CN/docs/Web/SVG/Introduction

<html>
  <body>
  <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
      <path d="M28.44......."/>
    </g>
  </svg>
</html>

如果您想将SVG保存在文件中,则CSS需要在SVG文件内定义。

您可以使用style标签实现:

http://www.w3.org/TR/SVG/styling.html#StyleElementExample

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
     width="50px" height="50px" viewBox="0 0 50 50">
  <defs>
    <style type="text/css"><![CDATA[
      .socIcon g {
        fill:red;
      }
    ]]></style>
  </defs>
  <g>
    <path d="M28.44......./>
  </g>
</svg>

你可以在服务器端使用工具来根据活动样式更新样式标签。在Ruby中,您可以使用Nokogiri实现此目的。SVG只是XML,因此可能有许多可用的XML库可以实现此目的。

如果您无法这样做,您将不得不像使用PNG一样使用它们;为每个样式创建一个集合,并将其样式保存在内联中。


26
这是否意味着没有方法可以通过缓存SVG并应用不同的样式来获得好处?内联似乎不能很好地缓存,而其他方法则需要创建许多图像版本,消除了缓存它们的任何好处。 - msg45f
另一种方法是将SVG编码为背景图像数据URI,每个版本使用不同的颜色,并依靠gzip来减小文件大小以消除重复。 - Ruskin
2
在我的情况下,我想覆盖SVG中的元素样式。我的CSS不起作用,因为元素样式具有更高的优先级。最简单的解决方案是为SVG的CSS样式添加!important。然后一切都好了。 如果要避免使用!important,则需要将元素样式移入CSS中。 - David Gausmann
4
@clayRay,一旦SVG2完成草案https://www.w3.org/TR/SVG2/styling.html#LinkElement,您就可以用这种方式完成它。请注意,本次翻译并没有改变原文的意思,只是将其转换为通俗易懂的中文。 - raddrick
1
@raddrick 太棒了!所以只是等待SVG2的事情。那么我们应该在2050年代的某个时候准备就绪了。我相信给我们带来《Duke Nukem Forever》的团队正在抓住这个机会。 - Mentalist
显示剩余2条评论

67

你可以自由操作,但有一个(重要的)限制:你无法通过外部 CSS 独立地为符号中的路径设置样式 —— 只能使用这种方法为整个符号设置属性。因此,如果你在符号中有两条路径并希望它们具有不同的填充颜色,则此方法将不起作用;但如果你希望所有路径都相同,则应该可以。

在你的 HTML 文件中,你需要像这样:

<style>
  .fill-red { fill: red; }
  .fill-blue { fill: blue; }
</style>

<a href="//www.example.com/">
  <svg class="fill-red">
    <use xlink:href="images/icons.svg#example"></use>
  </svg>
</a>

而在你想要的外部SVG文件中,你需要像这样:

<svg xmlns="http://www.w3.org/2000/svg">
   <symbol id="example" viewBox="0 0 256 256">
    <path d="M120...." />
  </symbol>
</svg>

将您的HTML中svg标签上的类从fill-red更改为fill-blue,然后您就会得到蓝色而不是红色。

通过在特定路径上混合和匹配外部CSS与某些内联CSS,您可以部分解决仅能单独定位路径的限制,因为内联CSS将优先。如果您正在做的是白色图标放在彩色背景上的事情,其中您想通过外部CSS更改背景颜色,但图标本身始终是白色(或反之亦然),那么这种方法将起作用。所以,使用与之前相同的HTML和类似于此的SVG代码,您将获得红色背景和白色前景路径:

<svg xmlns="http://www.w3.org/2000/svg">
  <symbol id="example" viewBox="0 0 256 256">
    <path class="background" d="M120..." />
    <path class="icon" style="fill: white;" d="M20..." />
  </symbol>
</svg>

2
很好的回答.. 我认为应该有一个警告: 浏览器支持!好的参考资料(比 caniuse 更详细):https://css-tricks.com/svg-fragment-identifiers-work/ - ptim
4
这是一个解决方案!实际上,没有必要将整个SVG内容包装在'symbol'元素中,即您只需为SVG元素提供一个'id',如下所示:<svg id=example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <path class="background" d="M120..." /> <path class="icon" style="fill: white;" d="M20..." /> </svg> - SimoneMSR
xlink:href现已被弃用。请改用href(可选附带xlink:href作为备用)。 - anroesti
如果您的SVG没有显示出来,可能是因为SVG缺少了viewBox。对我来说,这个方法解决了问题:<svg viewBox="0 0 780 540" class="fill-red"> 参见这里 - Matthias Braun

38

您可以使用以下方式在SVG文件中包含链接到外部CSS文件:

<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="mystyles.css" type="text/css"/>
您需要把这个放在开标签之后:
<svg>
  <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="mystyles.css" type="text/css"/>
  <g>
    <path d=.../>
  </g>
</svg>

这不是完美的解决方案,因为您必须修改svg文件,但是您只需修改一次即可,然后所有样式更改都可以在一个css文件中完成,适用于所有svg文件。


3
哇,这个方法可行,但只有1个点赞...这种解决方案适用于所有情况吗?它非常简单,为什么不是被选中的答案呢? - Bruno Vincent
整个重点是集中您的样式定义。假设您有10个SVG需要进行样式设置。现在,您需要将对CSS的引用复制到所有需要受影响的SVG中。如果CSS文件的文件名/位置更改,则需要在10个SVG中更新它。与对物理CSS文件的引用相比,CSS类感觉更具象征意义。 - Frans
12
注意,通过<img>标记加载的svg将不会加载外部内容(例如样式表)。 - Moose Morals
2
@Frans,你所提到的一切也同样适用于HTML,因此这并非SVG特有的。实际上这是一个非常普遍的链接问题——在文档之间标记链接确实有优点和缺点。 - Armen Michaeli
1
正如Moose Morals所说,使用这种方法时,我们无法在SVG内部使用<img>标签和加载外部CSS;因此,我建议使用<object>,并使用data属性而不是src - BarryCap

11

通过在JavaScript中动态创建样式元素并将其附加到SVG元素上,可以对SVG进行样式设置。方法有点不正规,但是它能够运行。

<object id="dynamic-svg" type="image/svg+xml" data="your-svg.svg">
    Your browser does not support SVG
</object>
<script>
    var svgHolder = document.querySelector('object#dynamic-svg');
    svgHolder.onload = function () {
        var svgDocument = svgHolder.contentDocument;
        var style = svgDocument.createElementNS("http://www.w3.org/2000/svg", "style");

        // Now (ab)use the @import directive to load make the browser load our css
        style.textContent = '@import url("/css/your-dynamic-css.css");';

        var svgElem = svgDocument.querySelector('svg');
        svgElem.insertBefore(style, svgElem.firstChild);
    };
</script>

如果您愿意,可以在PHP中动态生成JavaScript——JavaScript具有这种可能性,因此开启了无数的可能性。


嘿,我真的很喜欢你的解决方案,它也起作用了,但是我需要根据我的情况进行调整,如果你愿意帮忙的话。我在<defs>标签内有一个样式标签,我可以手动删除它们并运行此代码,这样它就会创建一个样式。是否有一种方法可以删除defs然后像您所做的那样重新创建元素或仅更新它?另外,url出现错误,未定义url,请您帮忙看看,谢谢。 - Kamel Mili

9

您可以采用一种方法,即使用CSS过滤器来更改浏览器中SVG图形的外观。

例如,如果您有一个在SVG代码内使用红色填充颜色的SVG图形,则可以通过设置hue-rotate为180度将其变为紫色:

#theIdOfTheImgTagWithTheSVGInIt {
    filter: hue-rotate(180deg);
    -webkit-filter: hue-rotate(180deg);
    -moz-filter: hue-rotate(180deg);
    -o-filter: hue-rotate(180deg);
    -ms-filter: hue-rotate(180deg);
}

尝试其他色相旋转设置,以找到所需的颜色。

需要明确的是,上述CSS代码应放在应用于HTML文档的CSS中。您正在为HTML代码中的img标签添加样式,而不是为SVG代码添加样式。

请注意,这无法处理填充为黑色、白色或灰色的图形。必须在其中有实际的颜色才能旋转该颜色的色相。


1
我来这里希望能找到类似的东西。哇,真是太棒了! - BBaysinger

6

通过首先内联外部SVG图像,应该可以做到。下面的代码来自Jess Frazelle的将所有SVG图像替换为内联SVG

$('img.svg').each(function(){
  var $img = $(this);
  var imgID = $img.attr('id');
  var imgClass = $img.attr('class');
  var imgURL = $img.attr('src');
  $.get(imgURL, function(data) {
    // Get the SVG tag, ignore the rest
    var $svg = $(data).find('svg');
    // Add replaced image's ID to the new SVG
    if (typeof imgID !== 'undefined') {
      $svg = $svg.attr('id', imgID);
    }
    // Add replaced image's classes to the new SVG
    if (typeof imgClass !== 'undefined') {
      $svg = $svg.attr('class', imgClass+' replaced-svg');
    }
    // Remove any invalid XML tags as per http:validator.w3.org
    $svg = $svg.removeAttr('xmlns:a');
    // Replace image with new SVG
    $img.replaceWith($svg);
  });
});

4
请注意,这仅在您将图像托管在与HTML相同的域上或者在图像服务器上有特别配置的跨域策略时才有效。如果没有有效的allow-access header,则$.get将使用ajax并无法从外部服务器加载图像。 - user151496
这是传奇。 - Tino Costa 'El Nino'

4

如果您使用<object>标记嵌入SVG,则可以使用外部CSS样式表来实现动态样式的快速解决方案。

此示例将在单击父元素时向根<svg>标记添加类。

file.svg:

<?xml-stylesheet type="text/css" href="../svg.css"?>
 <svg xmlns="http://www.w3.org/2000/svg" viewBox="">
  <g>
   <path/>
  </g>
 </svg>

html :

<a class="parent">
  <object data="file.svg"></object>
</a>

Jquery :

$(function() {
  $(document).on('click', '.parent', function(){
    $(this).find('object').contents().find('svg').attr("class","selected");
  }
});

点击父元素:

 <svg xmlns="http://www.w3.org/2000/svg" viewBox="" class="selected">

然后您可以管理您的CSS。
svg.css:
path {
 fill:none;
 stroke:#000;
 stroke-miterlimit:1.41;
 stroke-width:0.7px;
}

.selected path {
 fill:none;
 stroke:rgb(64, 136, 209);
 stroke-miterlimit:1.41;
 stroke-width:0.7px;
}

1
似乎不起作用,你能添加一个可行的示例吗? - Jeanluca Scaljeri

3
  1. 针对外部样式

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">

  <style>
 @import url(main.css);
  </style>

  <g>
    <path d="M28.44......./>
  </g>
</svg>

  1. 针对内部样式

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">

  <style>
     .socIcon g {fill:red;}
  </style>

  <g>
    <path d="M28.44......./>
  </g>
</svg>

注意: 如果您在<img>标记中包含SVG,则外部样式将无效。 它可以在<div>标记内完美工作。

<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="main.css" type="text/css"/> 也可以工作。 - BarryCap

2
对我有用的方法:使用带有@import规则的样式标签。
<defs>
    <style type="text/css">
        @import url("svg-common.css");
    </style>
</defs>

2
<image>标签中使用SVG必须将其包含在单个文件中以保护隐私。这个bugzilla bug提供了更多关于为什么这样做的详细信息。不幸的是,您不能使用其他标签,比如<iframe>,因为那样就无法作为链接工作,所以您必须在文件本身中嵌入CSS代码,使用<style>标签。

另一种方法是在主html文件中包含SVG数据。

<a href='http://youtube.com/...' target='_blank'>
  <svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
        <path d="M28.44......./>
    </g>
  </svg>
</a>

你可以使用 HTML 的 <link> 标签来链接一个外部的 CSS 文件来为其添加样式。

2
我无法将样式放在文件中。实际上,我要根据用户为我的网站选择的颜色方案更改这些图像的颜色。我目前的实现方式是向主页面添加样式表,覆盖默认样式。我想把颜色更改放在那个文件中,以影响嵌入的SVG图形。但是这样做行不通,因为我必须从SVG文件内部链接到样式表(除非还有一种方法可以使用JavaScript将该链接添加到所有SVG文件中)。肯定有一种方法可以允许外部SVG文件、外部CSS、链接和工具提示。 - Jordan H
1
由于浏览器的安全模型,您想要做的事情是不可能的。当SVG用作图像时,您无法使用JavaScript来操作它。将SVG用作图像时,请将其视为一个动画PNG或GIF文件,所有内容都在一个文件中且没有脚本访问权限。 - Robert Longson

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