466 votes

Comment changer la couleur d'une image SVG à l'aide de CSS (remplacement d'image SVG par jQuery) ?

Il s'agit d'une auto-questionnaire d'un morceau de code pratique que j'ai créé.

Actuellement, il n'existe pas de moyen simple d'intégrer une image SVG et d'avoir ensuite accès aux éléments SVG via CSS. Il existe plusieurs méthodes permettant d'utiliser des cadres JS SVG, mais elles sont trop compliquées si tout ce que vous faites est de créer une simple icône avec un état de renversement.

Voici donc ce que j'ai mis au point, qui, à mon avis, est de loin le moyen le plus simple d'utiliser des fichiers SVG sur un site web. Son concept s'inspire des premières méthodes de remplacement du texte par l'image, mais pour autant que je sache, cela n'a jamais été fait pour les SVG.

C'est la question :

Comment intégrer un SVG et changer sa couleur en CSS sans utiliser un framework JS-SVG ?

2 votes

Malheureusement, la balise img ne fonctionne pas avec les fichiers svg dans IE, il faut donc en tenir compte. IE reconnaît les balises embed. Quoi qu'il en soit, bon travail !

3 votes

Pour les svg, vous devez utiliser la propriété css "fill". Pour les images, il est approprié d'utiliser "filter". En fait, "Filter" fonctionne pour les deux, mais il est inutile de faire tout ce travail pour un graphique vectoriel.

23voto

AhrenFullStop Points 29

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

Explication :

Je suppose que vous avez html quelque chose comme ça :

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

Il faut absolument passer par le filtre, c'est-à-dire que votre svg est probablement noir ou blanc. Vous pouvez appliquer un filtre pour obtenir la couleur que vous voulez, par exemple, j'ai un svg noir que je veux vert menthe. Je l'inverse d'abord pour qu'il soit blanc (ce qui est techniquement toutes les couleurs RVB en plein) puis je joue avec la saturation de la teinte, etc. Pour y arriver :

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

Mieux encore, vous pouvez utiliser un outil qui convertit l'hexagone que vous souhaitez en un filtre : https://codepen.io/sosuke/pen/Pjoqqp

19voto

Max Points 89

@Drew Baker a donné une excellente solution pour résoudre le problème. Le code fonctionne correctement. Cependant, ceux qui utilisent AngularJs peuvent trouver beaucoup de dépendance à jQuery. Par conséquent, j'ai pensé que c'était une bonne idée de coller pour les utilisateurs d'AngularJS, un code suivant la solution de @Drew Baker.

Version AngularJs du même code

1. Html Utilisez la balise ci-dessous dans votre fichier html :

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

2. Directive : ce sera la directive dont vous aurez besoin pour reconnaître la balise :

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

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

4. Test unitaire avec 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]);
  });
});

19voto

DShultz Points 1293

Je me rends compte que vous voulez réaliser cela avec CSS, mais je vous rappelle qu'au cas où il s'agirait d'une petite image simple, vous pouvez toujours l'ouvrir dans Notepad++ et modifier le remplissage du path/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"/>

Ça pourrait économiser une tonne d'affreux script. Désolé si c'est hors sujet, mais parfois les solutions simples peuvent être négligées.

...même l'échange de plusieurs images svg pourrait être plus petit en taille que certains des extraits de code pour cette question.

8voto

Omri Aharon Points 14199

J'ai écrit une directive pour résoudre ce problème avec AngularJS. Elle est disponible ici - ngReusableSvg .

Il remplace l'élément SVG après qu'il ait été rendu, et le place à l'intérieur d'une balise div ce qui rend son CSS facilement modifiable. Cela permet d'utiliser le même fichier SVG à différents endroits en utilisant différentes tailles/couleurs.

L'utilisation est 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>

Après cela, vous pouvez facilement avoir :

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

6voto

Simon_Weaver Points 31141

Voici une version pour knockout.js sur la base de la réponse acceptée :

Important : En fait, il faut aussi utiliser jQuery pour le remplacement, mais j'ai pensé que cela pourrait être utile à certains.

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

        }
    };

Ensuite, il suffit d'appliquer data-bind="svgConvert: true" à votre balise img.

Cette solution remplace complètement le img avec un SVG et toute liaison supplémentaire ne serait pas respectée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X