it-roy-ru.com

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

Это самостоятельные вопросы и ответы о полезном куске кода, который я придумал.

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

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

Это вопрос:

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

410
Drew Baker

Во-первых, используйте тег 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'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');

        });

Вышеприведенный код выполняет поиск всех 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

516
Drew Baker

Стиль

svg path {
    fill: #000;
}

Скрипт

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

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

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

Это делает его светло-серым в большинстве браузеров. В IE (и, возможно, в Opera Mini, которую я не тестировал) оно заметно исчезает из-за свойства непрозрачности, которое все еще выглядит довольно хорошо, хотя и не серого цвета.

Вот пример с четырьмя различными классами CSS для значка Twemoji bell: original (желтый), вышеуказанный «отключенный» класс, 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>

27
aldel

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

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

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

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

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

Вам просто нужно использовать правило CSS, как это:

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

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

24
trebor1979

@Drew Baker дал отличное решение для решения проблемы. Код работает правильно. Тем не менее, те, кто использует AngularJ, могут найти большую зависимость от 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]);
  });
});
18
Max

Я понимаю, что вы хотите сделать это с помощью 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"/>

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

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

12
DShultz

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

Вот код без фреймворка, только чистый 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)
        })
4
user3144480

Вот версия для 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');

        }
    };

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

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

4
Simon_Weaver

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

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

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

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

Чтобы окончательно установить правило для SVG-потомков контейнера на 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 (сборка официальная) (64 бит) Linux

 enter image description here

  1. Опера 53.0.2907.37 Linux

 enter image description here

3
Benjamin

Существует библиотека с открытым исходным кодом под названием 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, чтобы избежать многократного использования одного и того же идентификатора в документе, если SVG вводится более одного раза. 

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

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

3
Waruyama

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


Например.

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

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

Здесь эффект ролловера с использованием чистого 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>

Вы можете конвертировать ваш SVG в URL-адрес данных здесь

  1. https://codepen.io/elliz/full/ygvgay
  2. https://websemantics.uk/tools/svg-to-background-image-conversion/
2
patelarpan

Выбранное решение подойдет, если вы хотите, чтобы 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();
1
AndroidDev

для анимации событий при наведении мыши мы можем оставить стили внутри файла 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

0
Aleksandr Ivanitskiy