Как изменить цвет изображения SVG с помощью CSS (замена изображения SVG jQuery)?

Это сам Q & A из удобного кода, с которым я пришел.

В настоящее время нет простого способа встраивания SVG-изображения, а затем доступ к элементам SVG через CSS. Существуют различные методы использования JS SVG-фреймворков, но они слишком сложны, если все, что вы делаете, это простой значок с состоянием опрокидывания.

Итак, вот что я придумал, и я считаю, что это самый простой способ использовать SVG файлы на веб-сайте. Он берет свою концепцию из ранних методов замены текста на изображение, но, насколько мне известно, никогда не было сделано для SVG.

Это вопрос:

Как встроить SVG и изменить его цвет в CSS без использования структуры JS-SVG?

Ответ 1

Во-первых, используйте тег IMG в своем HTML для встраивания SVG-графики. Я использовал Adobe Illustrator для создания графики.

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

Это так же, как вы бы встраивали нормальное изображение. Обратите внимание, что вам нужно установить IMG для класса svg. Класс social-link предназначен только для примера. Идентификатор не обязателен, но полезен.

Затем используйте этот код jQuery (в отдельном файле или в заголовке в заголовке).

    /**
     * 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 ID to the new SVG
                if(typeof imgID !== 'undefined') {
                    $svg = $svg.attr('id', imgID);
                }
                // Add replaced image 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');

        });

То, что делает приведенный выше код, ищет все IMG с классом 'svg' и заменяет его встроенным SVG из связанного файла. Огромное преимущество заключается в том, что теперь вы можете использовать CSS для изменения цвета SVG, например:

svg:hover path {
    fill: red;
}

Код jQuery, который я написал, также переносится через исходный идентификатор изображений и классы. Так что этот CSS тоже работает:

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

Или:

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

Вы можете увидеть пример того, как это работает здесь: 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

Ответ 2

Стиль

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

Ответ 3

Теперь вы можете использовать свойство CSS filter в большинстве современных браузеров (включая Edge, но не IE11). Он работает с изображениями SVG и другими элементами. Вы можете использовать hue-rotate или invert для изменения цветов, хотя они не позволяют вам изменять разные цвета независимо друг от друга. Я использую следующий класс CSS, чтобы показать "отключенную" версию значка (где оригинал представляет собой изображение SVG с насыщенным цветом):

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

Это делает его светло-серым в большинстве браузеров. В 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%);
}
<!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>

Ответ 4

В качестве альтернативы вы можете использовать CSS mask, предоставленную поддержку браузера не очень хорошо, но вы можете использовать резервный

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

Ответ 5

Если вы можете включить в свою версию файлы (включая PHP или включить через ваш CMS), вы можете добавить SVG-код и включить его на свою страницу. Это работает так же, как вставка источника SVG на страницу, но делает очистку страницы более чистым.

Преимущество состоит в том, что вы можете нацелить части своего SVG с помощью CSS для зависания - не требуется javascript.

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

Вам просто нужно использовать правило CSS следующим образом:

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

Обратите внимание, что бит !important необходим для переопределения цвета заливки.

Ответ 6

@Drew Baker дал отличное решение для решения проблемы. Код работает правильно. Тем не менее, те, кто использует AngularJs, могут найти большую зависимость от jQuery. Следовательно, я подумал, что неплохо вставить для пользователей AngularJS код, следующий за решением @Drew Baker.

Метод AngularJs одного и того же кода

1. Html: используйте тег ниже в файле html:

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

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:

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

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

Ответ 7

Я понимаю, что вы хотите выполнить это с помощью CSS, но просто напоминание на случай, если это маленький, простой образ - вы всегда можете открыть его в Notepad++ и изменить путь /whatelement fill:

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

Это могло бы сэкономить массу уродливого сценария. Извините, если это вне базы, но иногда простые решения можно упустить.

... даже замена нескольких изображений svg может быть меньше по размеру, чем некоторые фрагменты кода для этого вопроса.

Ответ 8

Я написал директиву для решения этой проблемы с помощью 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>

После этого вы можете легко:

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

Ответ 9

Здесь версия для 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 ID to the new SVG
                if (typeof imgID !== 'undefined')
                {
                    $svg = $svg.attr('id', imgID);
                }
                // Add replaced image 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');

        }
    };

Затем просто примените data-bind="svgConvert: true" к вашему тегу img.

Это решение полностью заменяет тег img SVG, и любые дополнительные привязки не будут соблюдаться.

Ответ 10

Здесь нет кода рамки, только чистый js:

document.querySelectorAll('img.svg').forEach(function(element) {
            var imgID = element.getAttribute('id')
            var imgClass = element.getAttribute('class')
            var imgURL = element.getAttribute('src')

            xhr = new XMLHttpRequest()
            xhr.onreadystatechange = function() {
                if(xhr.readyState == 4 && xhr.status == 200) {
                    var svg = xhr.responseXML.getElementsByTagName('svg')[0];

                    if(imgID != null) {
                         svg.setAttribute('id', imgID);
                    }

                    if(imgClass != null) {
                         svg.setAttribute('class', imgClass + ' replaced-svg');
                    }

                    svg.removeAttribute('xmlns:a')

                    if(!svg.hasAttribute('viewBox') && svg.hasAttribute('height') && svg.hasAttribute('width')) {
                        svg.setAttribute('viewBox', '0 0 ' + svg.getAttribute('height') + ' ' + svg.getAttribute('width'))
                    }
                    element.parentElement.replaceChild(svg, element)
                }
            }
            xhr.open('GET', imgURL, true)
            xhr.send(null)
        })

Ответ 11

Существует библиотека с открытым исходным кодом под названием SVGInject, которая использует атрибут onload для запуска инъекции. Проект GitHub можно найти на странице https://github.com/iconfu/svg-inject

Ниже приведен минимальный пример использования SVGInject:

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

После загрузки изображения onload="SVGInject(this) вызовет инъекцию, а элемент <img> будет заменен содержимым SVG файла, указанного в атрибуте src.

Он решает несколько проблем с инъекцией SVG:

  1. SVG можно скрыть до завершения инъекции. Это важно, если стиль уже применяется во время загрузки, что в противном случае могло бы привести к кратковременной "вспышке без использования содержимого".

  2. Элементы <img> автоматически внедряют их. Если вы добавляете SVG динамически, вам не придется беспокоиться о вызове функции впрыска снова.

  3. Случайная строка добавляется к каждому идентификатору в SVG, чтобы избежать повторения одного и того же ID в документе, если SVG вводится несколько раз.

SVGInject - это простой Javascript и работает со всеми браузерами, поддерживающими SVG.

Отказ от ответственности: я являюсь соавтором SVGInject

Ответ 12

Если у нас есть большее количество таких svg-изображений, мы также можем воспользоваться файлами-шрифтами.
Такие сайты, как https://glyphter.com/, могут получить файл шрифтов из наших svgs.


например.

@font-face {
    font-family: 'iconFont';
    src: url('iconFont.eot');
}
#target{
    color: white;
    font-size:96px;
    font-family:iconFont;
}

Ответ 13

Вы можете использовать для этого образ данных. используя data-image (data-URI), вы можете получить доступ к SVG, например, inline.

Вот эффект опрокидывания с использованием чистого CSS и SVG.

Я знаю, что это беспорядочно, но вы можете сделать это.

 .action-btn {
    background-size: 20px 20px;
    background-position: center center;
    background-repeat: no-repeat;
    border-width: 1px;
    border-style: solid;
    border-radius: 30px;
    height: 40px;
    width: 60px;
    display: inline-block;
 }

.delete {
     background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg version='1.1' id='Capa_1' fill='#FB404B' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='482.428px' height='482.429px' viewBox='0 0 482.428 482.429' style='enable-background:new 0 0 482.428 482.429;' xml:space='preserve'%3e%3cg%3e%3cg%3e%3cpath d='M381.163,57.799h-75.094C302.323,25.316,274.686,0,241.214,0c-33.471,0-61.104,25.315-64.85,57.799h-75.098 c-30.39,0-55.111,24.728-55.111,55.117v2.828c0,23.223,14.46,43.1,34.83,51.199v260.369c0,30.39,24.724,55.117,55.112,55.117 h210.236c30.389,0,55.111-24.729,55.111-55.117V166.944c20.369-8.1,34.83-27.977,34.83-51.199v-2.828 C436.274,82.527,411.551,57.799,381.163,57.799z M241.214,26.139c19.037,0,34.927,13.645,38.443,31.66h-76.879 C206.293,39.783,222.184,26.139,241.214,26.139z M375.305,427.312c0,15.978-13,28.979-28.973,28.979H136.096 c-15.973,0-28.973-13.002-28.973-28.979V170.861h268.182V427.312z M410.135,115.744c0,15.978-13,28.979-28.973,28.979H101.266 c-15.973,0-28.973-13.001-28.973-28.979v-2.828c0-15.978,13-28.979,28.973-28.979h279.897c15.973,0,28.973,13.001,28.973,28.979 V115.744z'/%3e%3cpath d='M171.144,422.863c7.218,0,13.069-5.853,13.069-13.068V262.641c0-7.216-5.852-13.07-13.069-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C158.074,417.012,163.926,422.863,171.144,422.863z'/%3e%3cpath d='M241.214,422.863c7.218,0,13.07-5.853,13.07-13.068V262.641c0-7.216-5.854-13.07-13.07-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C228.145,417.012,233.996,422.863,241.214,422.863z'/%3e%3cpath d='M311.284,422.863c7.217,0,13.068-5.853,13.068-13.068V262.641c0-7.216-5.852-13.07-13.068-13.07 c-7.219,0-13.07,5.854-13.07,13.07v147.154C298.213,417.012,304.067,422.863,311.284,422.863z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e ");
     border-color:#FB404B;
     
 }
 
 .delete:hover {
     background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg version='1.1' id='Capa_1' fill='#fff' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='482.428px' height='482.429px' viewBox='0 0 482.428 482.429' style='enable-background:new 0 0 482.428 482.429;' xml:space='preserve'%3e%3cg%3e%3cg%3e%3cpath d='M381.163,57.799h-75.094C302.323,25.316,274.686,0,241.214,0c-33.471,0-61.104,25.315-64.85,57.799h-75.098 c-30.39,0-55.111,24.728-55.111,55.117v2.828c0,23.223,14.46,43.1,34.83,51.199v260.369c0,30.39,24.724,55.117,55.112,55.117 h210.236c30.389,0,55.111-24.729,55.111-55.117V166.944c20.369-8.1,34.83-27.977,34.83-51.199v-2.828 C436.274,82.527,411.551,57.799,381.163,57.799z M241.214,26.139c19.037,0,34.927,13.645,38.443,31.66h-76.879 C206.293,39.783,222.184,26.139,241.214,26.139z M375.305,427.312c0,15.978-13,28.979-28.973,28.979H136.096 c-15.973,0-28.973-13.002-28.973-28.979V170.861h268.182V427.312z M410.135,115.744c0,15.978-13,28.979-28.973,28.979H101.266 c-15.973,0-28.973-13.001-28.973-28.979v-2.828c0-15.978,13-28.979,28.973-28.979h279.897c15.973,0,28.973,13.001,28.973,28.979 V115.744z'/%3e%3cpath d='M171.144,422.863c7.218,0,13.069-5.853,13.069-13.068V262.641c0-7.216-5.852-13.07-13.069-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C158.074,417.012,163.926,422.863,171.144,422.863z'/%3e%3cpath d='M241.214,422.863c7.218,0,13.07-5.853,13.07-13.068V262.641c0-7.216-5.854-13.07-13.07-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C228.145,417.012,233.996,422.863,241.214,422.863z'/%3e%3cpath d='M311.284,422.863c7.217,0,13.068-5.853,13.068-13.068V262.641c0-7.216-5.852-13.07-13.068-13.07 c-7.219,0-13.07,5.854-13.07,13.07v147.154C298.213,417.012,304.067,422.863,311.284,422.863z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e ");        
     background-color: #FB404B;
    }
<a class="action-btn delete">&nbsp;</a>

Ответ 14

Поскольку SVG - это в основном код, вам нужно просто содержимое. Я использовал PHP для получения контента, но вы можете использовать все, что захотите.

<?php
$content    = file_get_contents($pathToSVG);
?>

Затем я печатал содержимое "как есть" внутри контейнера div

<div class="fill-class"><?php echo $content;?></div>

Чтобы финализировать правило для контейнеров SVG childs на CSS

.fill-class > svg { 
    fill: orange;
}

Я получил эти результаты с помощью значка материала SVG:

  1. Mozilla Firefox 59.0.2 (64-разрядная версия) Linux

enter image description here

  1. Google Chrome66.0.3359.181 (Build oficial) (64 бит) Linux

enter image description here

  1. Opera 53.0.2907.37 Linux

enter image description here

Ответ 15

Выбранное решение прекрасно, если вы хотите, чтобы jQuery обрабатывал все элементы svg в вашей DOM, а ваш DOM был разумного размера. Но если ваш DOM большой, и вы решите загружать части своего DOM динамически, действительно нет смысла пересканировать всю DOM только для обновления элементов svg. Вместо этого используйте плагин jQuery для этого:

/**
 * A jQuery plugin that loads an svg file and replaces the jQuery object with its contents.
 *
 * The path to the svg file is specified in the src attribute (which normally does not exist for an svg element).
 *
 * The width, height and class attributes in the loaded svg will be replaced by those that exist in the jQuery object's
 * underlying html. Note: All other attributes in the original element are lost including the style attribute. Place
 * any styles in a style class instead.
 */
(function ($) {
    $.fn.svgLoader = function () {
        var src = $(this).attr("src");
        var width = this.attr("width");
        var height = this.attr("height");
        var cls = this.attr("class");
        var ctx = $(this);

        // Get the svg file and replace the <svg> element.
        $.ajax({
            url: src,
            cache: false
        }).done(function (html) {
            let svg = $(html);
            svg.attr("width", width);
            svg.attr("height", height);
            svg.attr("class", cls);
            var newHtml = $('<a></a>').append(svg.clone()).html();
            ctx.replaceWith(newHtml);
        });

        return this;
    };

}(jQuery));

В html укажите элемент svg следующим образом:

<svg src="images/someSvgFile.svg" height="45" width="45" class="mySVGClass"/>

И примените плагин:

$(".mySVGClass").svgLoader();

Ответ 16

для анимации событий hover мы можем оставить стили внутри svg файла, например,

<svg xmlns="http://www.w3.org/2000/svg">
<defs>
  <style>
  rect {
    fill:rgb(165,225,75);
    stroke:none;
    transition: 550ms ease-in-out;
    transform-origin:125px 125px;
  }
  rect:hover {
    fill:rgb(75,165,225);
    transform:rotate(360deg);
  }
  </style>
</defs>
  <rect x='50' y='50' width='150' height='150'/>
</svg>

проверьте это на svgshare

Ответ 17

Если это статическое изменение, откройте SVG файл в Adobe Illustrator (или любой подходящий редактор SVG), измените цвет и сохраните его.