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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11978995/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 11:10:05  来源:igfitidea点击:

How to change color of SVG image using CSS (jQuery SVG image replacement)?

jquerycsssvg

提问by Drew Baker

This is a self Q&A of a handy piece of code I came up with.

这是我想出的一段方便的代码的自我问答。

Currently, there isn't an easy way to embed an SVG image and then have access to the SVG elements via CSS. There are various methods of using JS SVG frameworks, but they are overly complicated if all you are doing is making a simple icon with a rollover state.

目前,没有一种简单的方法可以嵌入 SVG 图像,然后通过 CSS 访问 SVG 元素。有多种使用 JS SVG 框架的方法,但是如果您所做的只是制作一个具有滚动状态的简单图标,那么它们就过于复杂了。

So here is what I came up with, which I think is by far the easiest way to use SVG files on a website. It takes its concept from the early text-to-image replacement methods, but as far as I am aware has never been done for SVGs.

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

This is the question:

这是问题:

How do I embed an SVG and change its color in CSS without using a JS-SVG framework?

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

回答by Drew Baker

Firstly, use an IMG tag in your HTML to embed an SVG graphic. I used Adobe Illustrator to make the graphic.

首先,在 HTML 中使用 IMG 标签来嵌入 SVG 图形。我使用 Adob​​e Illustrator 制作图形。

<img id="facebook-logo" class="svg social-link" src="/images/logo-facebook.svg"/>

This is just like how you'd embed a normal image. Note that you need to set the IMG to have a class of svg. The 'social-link' class is just for examples sake. The ID is not required, but is useful.

这就像您嵌入普通图像的方式一样。请注意,您需要将 IMG 设置为具有 svg 类。'social-link' 类只是为了举例。ID 不是必需的,但很有用。

Then use this jQuery code (in a separate file or inline in the HEAD).

然后使用此 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');

        });

What the above code does is look for all IMG's with the class 'svg' and replace it with the inline SVG from the linked file. The massive advantage is that it allows you to use CSS to change the color of the SVG now, like so:

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

svg:hover path {
    fill: red;
}

The jQuery code I wrote also ports across the original images ID and classes. So this CSS works too:

我编写的 jQuery 代码还移植了原始图像 ID 和类。所以这个 CSS 也有效:

#facebook-logo:hover path {
    fill: red;
}

Or:

或者:

.social-link:hover path {
    fill: red;
}

You can see an example of it working here: http://labs.funkhausdesign.com/examples/img-svg/img-to-svg.html

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

We have a more complicated version that includes caching here: https://github.com/funkhaus/style-guide/blob/master/template/js/site.js#L32-L90

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

回答by Henrik Albrechtsson

Style

风格

svg path {
    fill: #000;
}

Script

脚本

$(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');
    });
});

回答by aldel

You can now use the CSS filterpropertyin most modern browsers(including Edge, but not IE11). It works on SVG images as well as other elements. You can use hue-rotateor invertto modify colors, although they don't let you modify different colors independently. I use the following CSS class to show a "disabled" version of an icon (where the original is an SVG picture with saturated color):

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

.disabled {
    opacity: 0.4;
    filter: grayscale(100%);
    -webkit-filter: grayscale(100%);
}

This makes it light grey in most browsers. In IE (and probably Opera Mini, which I haven't tested) it is noticeably faded by the opacity property, which still looks pretty good, although it's not grey.

这使它在大多数浏览器中呈浅灰色。在 IE(可能还有 Opera Mini,我还没有测试过)中,它被 opacity 属性明显地淡化了,虽然它不是灰色的,但看起来仍然很好。

Here's an example with four different CSS classes for the Twemojibell icon: original (yellow), the above "disabled" class, hue-rotate(green), and invert(blue).

这是一个示例,其中包含Twemoji铃铛图标的四个不同 CSS 类:原始(黄色)、上述“已禁用”类、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%);
}
<!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>

回答by seanjacob

Alternatively you could use CSS mask, granted browser supportisn't good but you could use a fallback

或者,您可以使用 CSS mask,授予浏览器支持不好,但您可以使用回退

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

回答by trebor1979

If you can include files (PHP include or include via your CMS of choice) in your page, you can add the SVG code and include it into your page. This works the same as pasting the SVG source into the page, but makes the page markup cleaner.

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

The benefit is that you can target parts of your SVG via CSS for hover -- no javascript required.

好处是您可以通过 CSS 定位 SVG 的一部分以进行悬停——不需要 javascript。

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

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

You just have to use a CSS rule like this:

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

#pathidorclass:hover { fill: #303 !important; }

Note that the !importantbit is necessary to override the fill color.

请注意,该!important位是覆盖填充颜色所必需的。

回答by Max

@Drew Baker gave a great solution to solve the problem. The code works properly. However, those who uses AngularJs may find lots of dependency on jQuery. Consequently, I thought it is a good idea to paste for AngularJS users, a code following @Drew Baker's solution.

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

AngularJs way of the same code

相同代码的AngularJs方式

1. Html: use the bellow tag in you html file:

1. Html: 在你的 html 文件中使用波纹管标签:

<svg-image src="/icons/my.svg" class="any-class-you-wish"></svg-image>

2. Directive: this will be the directive that you will need to recognise the tag:

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));
        });
      }
    };
  }]);

3. CSS:

3.CSS

.any-class-you-wish{
    border: 1px solid red;
    height: 300px;
    width:  120px
}

4. Unit-test with karma-jasmine:

4. 使用 karma-jasmine 进行单元测试

'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]);
  });
});

回答by DShultz

I realize you're wanting to accomplish this with CSS, but just a reminder in case it's a small, simple image - you can always pop it open in Notepad++ and change the path/whateverelement's fill:

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

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

It could save a ton of ugly script. Sorry if it's off-base, but sometimes the simple solutions can be overlooked.

它可以节省大量丑陋的脚本。很抱歉,如果它不在基础上,但有时可以忽略简单的解决方案。

...even swapping multiple svg images might be smaller in size than some of the code snippets for this question.

...即使交换多个 svg 图像的大小也可能比此问题的某些代码片段小。

回答by Omri Aharon

I wrote a directive to solve this issue with AngularJS. It is available here - ngReusableSvg.

我写了一个指令来用 AngularJS 解决这个问题。它在此处可用- ngReusableSvg

It replaces the SVG element after it's been rendered, and places it inside a divelement, making its CSS easily changeable. This helps using the same SVG file in different places using different sizes/colors.

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

The usage is simple:

用法很简单:

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

After that, you can easily have:

之后,您可以轻松拥有:

.svg-class svg {
    fill: red; // whichever color you want
}

回答by AhrenFullStop

TL/DR: GO here-> https://codepen.io/sosuke/pen/Pjoqqp

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

Explanation:

解释:

I'm assuming you have html something like this:

我假设你有这样的 html:

<img src="/img/source.svg" class="myClass">

Definitely go the filter route, ie. your svg is most likely black or white. You can apply a filter to get it to be whatever color you want, for example, I have a black svg that I want mint green. I first invert it to be white (which is technically all RGB colors on full) then play with the hue saturation etc. To get it right:

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

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

Even better is that you could just use a tool to convert the hex you want into a filter for you: https://codepen.io/sosuke/pen/Pjoqqp

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

回答by Simon_Weaver

Here's a version for knockout.jsbased on the accepted answer:

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

Important:It does actually require jQuery too for the replacing, but I thought it may be useful to some.

重要提示:它实际上也需要 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');

        }
    };

Then just apply data-bind="svgConvert: true"to your img tag.

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

This solution completely replaces the imgtag with a SVG and any additional bindings would not be respected.

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