如何使用CSS(jQuery SVG图像替换)更改SVG图像的颜色?

Dre*_*ker 428 css jquery svg

这是我提出的一个方便的代码的自我问答.

目前,没有一种简单的方法来嵌入SVG图像,然后通过CSS访问SVG元素.有各种使用JS SVG框架的方法,但如果你所做的只是制作一个带有翻转状态的简单图标,它们就会过于复杂.

所以这就是我提出的,我认为这是迄今为止在网站上使用SVG文件的最简单方法.它从早期的文本到图像替换方法中采用了它的概念,但据我所知,SVG从未做过.

这是个问题:

如何在不使用JS-SVG框架的情况下嵌入SVG并在CSS中更改其颜色?

Dre*_*ker 529

首先,在HTML中使用IMG标记嵌入SVG图形.我使用Adobe Illustrator制作图形.

<img id="facebook-logo" class="svg social-link" src="/images/logo-facebook.svg"/>
Run Code Online (Sandbox Code Playgroud)

这就像你嵌入正常图像一样.请注意,您需要将IMG设置为具有svg类."社交链接"类只是为了举例.该ID不是必需的,但很有用.

然后使用这个jQuery代码(在单独的文件中或在HEAD中内联).

    /**
     * Replace all SVG images with inline SVG
     */
        jQuery('img.svg').each(function(){
            var $img = jQuery(this);
            var imgID = $img.attr('id');
            var imgClass = $img.attr('class');
            var imgURL = $img.attr('src');

            jQuery.get(imgURL, function(data) {
                // Get the SVG tag, ignore the rest
                var $svg = jQuery(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);

            }, 'xml');

        });
Run Code Online (Sandbox Code Playgroud)

上面的代码所做的是查找所有带有'svg'类的IMG,并将其替换为链接文件中的内联SVG.它的巨大优势在于它允许您现在使用CSS来更改SVG的颜色,如下所示:

svg:hover path {
    fill: red;
}
Run Code Online (Sandbox Code Playgroud)

我写的jQuery代码也跨越原始图像ID和类.所以这个CSS也适用:

#facebook-logo:hover path {
    fill: red;
}
Run Code Online (Sandbox Code Playgroud)

要么:

.social-link:hover path {
    fill: red;
}
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到它的一个例子:http: //labs.funkhausdesign.com/examples/img-svg/img-to-svg.html

我们有一个更复杂的版本,包括缓存:https: //github.com/funkhaus/style-guide/blob/master/template/js/site.js#L32-L90

  • 你甚至可以用`img [src $ =..svg"]`替换选择器,并且不需要`svg`类. (29认同)
  • 它有时可能不适用于Safari(例如),以确保返回的数据是可读的,重新加入`});`$ .get with`},'xml');` (6认同)
  • 有点晚了,@ LeonGaban,但更好的方法是完全删除fill属性,并依靠CSS文件向父svg添加填充.`$('#ico_company path').removeAttr('fill')`.然后你可以在CSS文件中执行`#ico_company {fill:#ccc}` (3认同)
  • @LeonGaban我认为没有办法定位背景图像的填充.如果可以的话,这将是非常有用的! (2认同)
  • @Soaku很容易让jQuery将所有路径设置为与字体颜色相同,例如`var color = $ img.closest('p').css('color'); $ svg.find('path').attr('fill',color); `但我认为这是一个更好的工作留给CSS. (2认同)
  • 触发注入的一个好方法是使用`<img>`元素中的`onload`属性.有关详细信息,请参阅我的答案:/sf/answers/3651915011/ (2认同)

Hen*_*son 55

样式

svg path {
    fill: #000;
}
Run Code Online (Sandbox Code Playgroud)

脚本

$(document).ready(function() {
    $('img[src$=".svg"]').each(function() {
        var $img = jQuery(this);
        var imgURL = $img.attr('src');
        var attributes = $img.prop("attributes");

        $.get(imgURL, function(data) {
            // Get the SVG tag, ignore the rest
            var $svg = jQuery(data).find('svg');

            // Remove any invalid XML tags
            $svg = $svg.removeAttr('xmlns:a');

            // Loop through IMG attributes and apply on SVG
            $.each(attributes, function() {
                $svg.attr(this.name, this.value);
            });

            // Replace IMG with SVG
            $img.replaceWith($svg);
        }, 'xml');
    });
});
Run Code Online (Sandbox Code Playgroud)

  • 如果您没有宽度属性,它只会创建一个带有错误数字的属性。在我的例子中为“width=”170.667“” (2认同)
  • 这不是完美的,因为它失去了以前的img尺寸. (2认同)
  • 请注意,如果您的 svg 也是由非“路径”形状(如“矩形”)制成的,您也需要在 css 中处理它们 (2认同)

ald*_*del 32

您现在可以在大多数现代浏览器中使用CSS filter属性(包括Edge,但不包括IE11).它适用于SVG图像以及其他元素.您可以使用或修改颜色,但它们不允许您单独修改不同的颜色.我使用以下CSS类来显示图标的"禁用"版本(其中原始版本是饱和色的SVG图片):hue-rotateinvert

.disabled {
    opacity: 0.4;
    filter: grayscale(100%);
    -webkit-filter: grayscale(100%);
}
Run Code Online (Sandbox Code Playgroud)

这使它在大多数浏览器中都呈现浅灰色.在IE(可能还有Opera Mini,我还没有测试过)中,不透明度属性明显褪色,虽然它不是灰色的,但仍然看起来很不错.

这是一个有两个不同的CSS类的示例,用于Twemoji铃声图标:原始(黄色),上面的"禁用"类,hue-rotate(绿色)和invert(蓝色).

.twa-bell {
  background-image: url("https://twemoji.maxcdn.com/svg/1f514.svg");
  display: inline-block;
  background-repeat: no-repeat;
  background-position: center center;
  height: 3em;
  width: 3em;
  margin: 0 0.15em 0 0.3em;
  vertical-align: -0.3em;
  background-size: 3em 3em;
}
.grey-out {
  opacity: 0.4;
  filter: grayscale(100%);
  -webkit-filter: grayscale(100%);
}
.hue-rotate {
  filter: hue-rotate(90deg);
  -webkit-filter: hue-rotate(90deg);
}
.invert {
  filter: invert(100%);
  -webkit-filter: invert(100%);
}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <span class="twa-bell"></span>
  <span class="twa-bell grey-out"></span>
  <span class="twa-bell hue-rotate"></span>
  <span class="twa-bell invert"></span>
</body>

</html>
Run Code Online (Sandbox Code Playgroud)


sea*_*cob 24

或者你可以使用CSS mask,授予浏览器支持不好但你可以使用后备

.frame {
    background: blue;
    -webkit-mask: url(image.svg) center / contain no-repeat;
}
Run Code Online (Sandbox Code Playgroud)

  • [MDN指定](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-mask),不应在任何生产网站上使用`-webkit-mask`. (13认同)
  • 不对 svg 着色 (2认同)

小智 23

如果您可以在页面中包含文件(PHP包括或通过您选择的CMS包含),您可以添加SVG代码并将其包含在您的页面中.这与将SVG源粘贴到页面中的工作方式相同,但使页面标记更清晰.

好处是你可以通过CSS定位部分SVG悬停 - 不需要javascript.

http://codepen.io/chriscoyier/pen/evcBu

你只需要使用这样的CSS规则:

#pathidorclass:hover { fill: #303 !important; }
Run Code Online (Sandbox Code Playgroud)

请注意,该!important位必须覆盖填充颜色.

  • 对于使用AngularJS的人:`<div ng-include ="'svg.svg'"> </ div> (3认同)

Max*_*Max 18

@Drew Baker为解决问题提供了很好的解决方案.代码工作正常.但是,那些使用AngularJs的人可能会发现很多依赖于jQuery.因此,我认为粘贴AngularJS用户是一个好主意,这是一个遵循@Drew Baker解决方案的代码.

AngularJs的代码方式相同

1. Html:在你的html文件中使用bellow标签:

<svg-image src="/icons/my.svg" class="any-class-you-wish"></svg-image>
Run Code Online (Sandbox Code Playgroud)

2.指令:这将是您需要识别标签的指令:

'use strict';
angular.module('myApp')
  .directive('svgImage', ['$http', function($http) {
    return {
      restrict: 'E',
      link: function(scope, element) {
        var imgURL = element.attr('src');
        // if you want to use ng-include, then
        // instead of the above line write the bellow:
        // var imgURL = element.attr('ng-include');
        var request = $http.get(
          imgURL,
          {'Content-Type': 'application/xml'}
        );

        scope.manipulateImgNode = function(data, elem){
          var $svg = angular.element(data)[4];
          var imgClass = elem.attr('class');
          if(typeof(imgClass) !== 'undefined') {
            var classes = imgClass.split(' ');
            for(var i = 0; i < classes.length; ++i){
              $svg.classList.add(classes[i]);
            }
          }
          $svg.removeAttribute('xmlns:a');
          return $svg;
        };

        request.success(function(data){
          element.replaceWith(scope.manipulateImgNode(data, element));
        });
      }
    };
  }]);
Run Code Online (Sandbox Code Playgroud)

3. CSS:

.any-class-you-wish{
    border: 1px solid red;
    height: 300px;
    width:  120px
}
Run Code Online (Sandbox Code Playgroud)

4.用业力茉莉花进行单元测试:

'use strict';

describe('Directive: svgImage', function() {

  var $rootScope, $compile, element, scope, $httpBackend, apiUrl, data;

  beforeEach(function() {
    module('myApp');

    inject(function($injector) {
      $rootScope = $injector.get('$rootScope');
      $compile = $injector.get('$compile');
      $httpBackend = $injector.get('$httpBackend');
      apiUrl = $injector.get('apiUrl');
    });

    scope = $rootScope.$new();
    element = angular.element('<svg-image src="/icons/icon-man.svg" class="svg"></svg-image>');
    element = $compile(element)(scope);

    spyOn(scope, 'manipulateImgNode').andCallThrough();
    $httpBackend.whenGET(apiUrl + 'me').respond(200, {});

    data = '<?xml version="1.0" encoding="utf-8"?>' +
      '<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->' +
      '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' +
      '<!-- Obj -->' +
      '<!-- Obj -->' +
      '<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"' +
      'width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">' +
        '<g>' +
          '<path fill="#F4A902" d=""/>' +
          '<path fill="#F4A902" d=""/>' +
        '</g>' +
      '</svg>';
    $httpBackend.expectGET('/icons/icon-man.svg').respond(200, data);
  });

  afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
  });

  it('should call manipulateImgNode atleast once', function () {
    $httpBackend.flush();
    expect(scope.manipulateImgNode.callCount).toBe(1);
  });

  it('should return correct result', function () {
    $httpBackend.flush();
    var result = scope.manipulateImgNode(data, element);
    expect(result).toBeDefined();
  });

  it('should define classes', function () {
    $httpBackend.flush();
    var result = scope.manipulateImgNode(data, element);
    var classList = ["svg"];
    expect(result.classList[0]).toBe(classList[0]);
  });
});
Run Code Online (Sandbox Code Playgroud)

  • 您的解决方案不起作用,可能是 `&lt;div ng-include="/icons/my.svg" class="any-class-you-wish"&gt;&lt;/div&gt;` (2认同)
  • 您的 IE 代码有问题。您可以只使用 `if (typeof(imgClass) !== 'undefined') { $svg.setAttribute("class", imgClass); }` 而不是 split 和 for 循环。 (2认同)
  • 很棒的工作!但是对于某些图像,你需要获取svg的第一个元素(`angular.element(data)[0];`)并使其与IE一起使用`if($ svg.getAttribute('class')){ $ svg.setAttribute('class',$ svg.getAttribute('class')+''+ imgClass); } else {$ svg.setAttribute('class',imgClass); }`.另外,您可能希望将`cache:true`添加到`$ http.get`的选项中,否则您的页面可能会变得非常慢. (2认同)

Ahr*_*top 15

TL/DR:去这里-> https://codepen.io/sosuke/pen/Pjoqqp

解释:

我假设你有这样的 html:

<img src="/img/source.svg" class="myClass">
Run Code Online (Sandbox Code Playgroud)

绝对走过滤路线,即。您的 svg 很可能是黑色或白色。您可以应用过滤器使其成为您想要的任何颜色,例如,我有一个黑色 svg,我想要薄荷绿色。我首先将它反转为白色(从技术上讲,所有 RGB 颜色都是完整的)然后玩色相饱和度等。为了让它正确:

filter: invert(86%) sepia(21%) saturate(761%) hue-rotate(92deg) brightness(99%) contrast(107%);
Run Code Online (Sandbox Code Playgroud)

更好的是,您可以使用一个工具将您想要的十六进制转换为您的过滤器:https : //codepen.io/sosuke/pen/Pjoqqp

  • 这是一个很棒的纯 CSS 解决方案,加上从十六进制到过滤器的 codepen 非常棒。 (2认同)
  • 高超!确切地说,我只是在寻找这个。 (2认同)

DSh*_*ltz 14

我意识到你想用CSS完成这个,但只是提醒一下,如果它是一个小而简单的图像 - 你总是可以在Notepad ++中打开它并改变路径/ whateverelement的填充:

<path style="fill:#010002;" d="M394.854,205.444c9.218-15.461,19.102-30.181,14.258-49.527
    ...
    C412.843,226.163,402.511,211.451,394.854,205.444z"/>
Run Code Online (Sandbox Code Playgroud)

它可以节省大量丑陋的剧本.对不起,如果它是偏离基础的,但有时可以忽略简单的解决方案.

...甚至交换多个svg图像的大小可能比这个问题的一些代码片段小.


Omr*_*ron 7

我写了一个指令来解决AngularJS的这个问题.它可以在这里找到 - ngReusableSvg.

它在渲染后替换SVG元素,并将其放置在div元素中,使其CSS易于更改.这有助于在不同的地方使用不同的尺寸/颜色使用相同的SVG文件.

用法很简单:

<object oa-reusable-svg
        data="my_icon.svg"
        type="image/svg+xml"
        class="svg-class"
        height="30"  // given to prevent UI glitches at switch time
        width="30">
</object>
Run Code Online (Sandbox Code Playgroud)

之后,您可以轻松拥有:

.svg-class svg {
    fill: red; // whichever color you want
}
Run Code Online (Sandbox Code Playgroud)


War*_*ama 6

有一个名为 SVGInject 的开源库,它使用该onload属性来触发注入。您可以在https://github.com/iconfu/svg-inject找到 GitHub 项目

这是使用 SVGInject 的最小示例:

<html>
  <head>
    <script src="svg-inject.min.js"></script>
  </head>
  <body>
    <img src="image.svg" onload="SVGInject(this)" />
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

加载图像后,onload="SVGInject(this)将触发注入,并且该<img>元素将被属性中提供的 SVG 文件的内容替换src

它解决了 SVG 注入的几个问题:

  1. SVG 可以隐藏,直到注入完成。如果在加载期间已经应用了样式,这一点很重要,否则会导致短暂的“无样式内容闪烁”。

  2. 元素<img>会自动注入。如果动态添加 SVG,则不必担心再次调用注入函数。

  3. 随机字符串会添加到 SVG 中的每个 ID,以避免在多次注入 SVG 时文档中多次出现相同的 ID。

SVGInject 是纯 Javascript,适用于所有支持 SVG 的浏览器。

免责声明:我是 SVGInject 的合著者


Sim*_*ver 5

这是knockout.js基于已接受答案的版本:

重要提示:它实际上也需要 jQuery 来替换,但我认为它可能对某些人有用。

ko.bindingHandlers.svgConvert =
    {
        'init': function ()
        {
            return { 'controlsDescendantBindings': true };
        },

        'update': function (element, valueAccessor, allBindings, viewModel, bindingContext)
        {
            var $img = $(element);
            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);

            }, 'xml');

        }
    };
Run Code Online (Sandbox Code Playgroud)

然后只适用data-bind="svgConvert: true"于您的 img 标签。

此解决方案将img标签完全替换为 SVG,并且不会遵守任何其他绑定。

  • 这很棒!如果您想让它更上一层楼,我们有一个包含缓存的更新版本,因此不会请求两次相同的 SVG。https://github.com/funkhaus/style-guide/blob/master/template/js/site.js#L32-L90 (2认同)
  • @DrewBaker实际上我更担心 img 标签会请求文件,然后“get”会再次请求它。我考虑过将“img”标签上的“src”更改为“data-src”属性,但得出的结论是,现代浏览器可能足够聪明,可以缓存该文件 (2认同)