470 Stimmen

Wie ändert man die Farbe eines SVG-Bildes mit CSS (jQuery SVG-Bildersatz)?

Dies ist eine Selbstbefragung zu einem praktischen Code, den ich mir ausgedacht habe.

Derzeit gibt es keine einfache Möglichkeit, ein SVG-Bild einzubetten und dann per CSS auf die SVG-Elemente zuzugreifen. Es gibt verschiedene Methoden zur Verwendung von JS-SVG-Frameworks, aber sie sind übermäßig kompliziert, wenn Sie nur ein einfaches Symbol mit einem Rollover-Status erstellen wollen.

Ich habe mir also folgendes ausgedacht, das meiner Meinung nach die bei weitem einfachste Möglichkeit ist, SVG-Dateien auf einer Website zu verwenden. Das Konzept stammt von den frühen Text-zu-Bild-Ersetzungsmethoden, aber soweit ich weiß, wurde es noch nie für SVGs verwendet.

Das ist die Frage:

Wie kann ich ein SVG einbetten und seine Farbe in CSS ändern, ohne ein JS-SVG-Framework zu verwenden?

24voto

AhrenFullStop Punkte 432

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

Erläuterung:

Ich nehme an, Sie haben so etwas wie html:

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

Gehen Sie auf jeden Fall die Filter-Route, dh Ihre svg ist höchstwahrscheinlich schwarz oder weiß. Sie können einen Filter anwenden, um eine beliebige Farbe zu erhalten, zum Beispiel habe ich eine schwarze svg, die ich mintgrün haben möchte. Ich invertiere es zunächst in Weiß (was technisch gesehen alle RGB-Farben auf Vollton ist) und spiele dann mit dem Farbton, der Sättigung usw. Um es richtig zu machen:

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

Noch besser ist, dass Sie einfach ein Tool verwenden können, das das gewünschte Hexadezimalfeld in einen Filter umwandelt: https://codepen.io/sosuke/pen/Pjoqqp

19voto

Max Punkte 2265

@Drew Baker hat eine großartige Lösung für dieses Problem gefunden. Der Code funktioniert einwandfrei. Diejenigen, die AngularJs verwenden, werden jedoch viele Abhängigkeiten von jQuery feststellen. Daher hielt ich es für eine gute Idee, für AngularJS-Nutzer einen Code einzufügen, der der Lösung von @Drew Baker folgt.

Die AngularJs-Variante des gleichen Codes

1. Html : Verwenden Sie den folgenden Tag in Ihrer HTML-Datei:

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

2. Richtlinie : Dies ist die Richtlinie, die Sie benötigen, um den Tag zu erkennen:

'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. Unit-Test mit 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 Punkte 4117

Mir ist klar, dass du das mit CSS erreichen willst, aber nur zur Erinnerung, falls es sich um ein kleines, einfaches Bild handelt - du kannst es jederzeit in Notepad++ öffnen und die Füllung des Pfad-/Whatever-Elements ändern:

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

Das könnte eine Menge hässlicher Skripte sparen. Sorry, wenn es off-Base, aber manchmal die einfachen Lösungen können übersehen werden.

...sogar das Austauschen mehrerer svg-Bilder könnte kleiner sein als einige der Codeschnipsel für diese Frage.

8voto

Omri Aharon Punkte 16749

Ich habe eine Richtlinie geschrieben, um dieses Problem mit AngularJS zu lösen. Sie ist verfügbar hier - ngReusableSvg .

Es ersetzt das SVG-Element, nachdem es gerendert wurde, und platziert es innerhalb einer div Element, wodurch sein CSS leicht geändert werden kann. Dies hilft bei der Verwendung der gleichen SVG-Datei an verschiedenen Stellen mit unterschiedlichen Größen/Farben.

Die Verwendung ist einfach:

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

Danach können Sie es einfach haben:

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

6voto

Simon_Weaver Punkte 129442

Hier ist eine Version für knockout.js auf der Grundlage der akzeptierten Antwort:

Wichtig: Es erfordert eigentlich jQuery auch für die Ersetzung, aber ich dachte, es kann nützlich sein, einige.

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

        }
    };

Dann wenden Sie einfach data-bind="svgConvert: true" zu Ihrem img-Tag.

Diese Lösung ersetzt vollständig die img Tag mit einer SVG und alle zusätzlichen Bindungen würden nicht beachtet werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X