Изменить цвет заливки SVG, когда он используется в качестве фонового изображения

270

Помещая вывод SVG прямо в линию с кодом страницы, я могу просто изменить цвета заливки с помощью CSS следующим образом:

polygon.mystar {
    fill: blue;
}​

circle.mycircle {
    fill: green;
}

Это прекрасно работает, однако я ищу способ изменить атрибут "fill" SVG, когда он используется как BACKGROUND-IMAGE.

html {      
    background-image: url(../img/bg.svg);
}

Как я могу изменить цвета сейчас? Это вообще возможно?

Для справки, вот содержимое моего внешнего файла SVG:

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     width="320px" height="100px" viewBox="0 0 320 100" enable-background="new 0 0 320 100" xml:space="preserve">
<polygon class="mystar" fill="#3CB54A" points="134.973,14.204 143.295,31.066 161.903,33.77 148.438,46.896 151.617,65.43 134.973,56.679 
    118.329,65.43 121.507,46.896 108.042,33.77 126.65,31.066 "/>
<circle class="mycircle" fill="#ED1F24" cx="202.028" cy="58.342" r="12.26"/>
</svg>
Джо
источник
Меня часто бьют реквизитом за мой ответ. Вы должны рассмотреть вопрос об изменении его на принятый ответ, чтобы он не был пропущен.
Райан

Ответы:

75

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

Тогда вы просто используете этот URL в вашем CSS.

Потому что как фоновое изображение, оно не является частью DOM, и вы не можете им манипулировать. Другой возможностью было бы использовать его регулярно, встраивать его в страницу обычным способом, но расположить его абсолютно, сделать его шириной и высотой страницы, а затем использовать свойство z-index css, чтобы поместить его позади всех других элементов DOM. на странице.

tonino.j
источник
7
Не забудьте, если вы обслуживаете SVG из серверного скрипта, чтобы убедиться, что вы также отправляете правильный заголовок MIME . В PHP это было бы:<?php header('Content-type: image/svg+xml'); ?>
Сол Fautley
4
Вы можете использовать изображение svg в качестве маски и управлять цветом фона элемента. Это будет иметь тот же эффект, что и изменение заливки. (подробный ответ предоставлен)
зарезервировано
16
Этот ответ был великолепен с 2012 года, но теперь CSS-маски и / или фильтры поддерживаются во всех браузерах в течение некоторого времени. Я рекомендую всем, кто читает это сейчас, проверить ссылки в ответе widged ниже или просто перейти к CSS Masks здесь , что является действительно простым решением - обратите внимание, что для него все еще требуется 2 версии правила, одна с префиксом -webkit- в настоящее время время. Для Microsoft Edge в настоящее время фильтры CSS поддерживаются, но еще не маскируются.
Джоэлхарди
2
В настоящее время существует множество решений, которые работают, и я согласен, что этот ответ больше не отражает текущее состояние возможностей.
Дэвид Нето
148

Мне нужно что-то подобное и хотел придерживаться CSS. Вот миксины LESS и SCSS, а также простой CSS, который может помочь вам в этом. К сожалению, поддержка браузера немного слабовата. Подробнее о поддержке браузера смотрите ниже.

Меньше миксин:

.element-color(@color) {
  background-image: url('data:image/svg+xml;utf8,<svg ...><g stroke="@{color}" ... /></g></svg>');
}

МЕНЬШЕЕ использование:

.element-color(#fff);

SCSS mixin:

@mixin element-color($color) {
  background-image: url('data:image/svg+xml;utf8,<svg ...><g stroke="#{$color}" ... /></g></svg>');
}

Использование SCSS:

@include element-color(#fff);

CSS:

// color: red
background-image: url('data:image/svg+xml;utf8,<svg ...><g stroke="red" ... /></g></svg>');

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

Райан
источник
Хотя есть некоторые накладные расходы, потому что я должен жестко закодировать все элементы SVG, я думаю, что это лучшее решение на некоторое время. Спасибо
Адриано Роза
Вам нужно использовать CSS-препроцессор, такой как Sass или Less, если вы хотите использовать таким образом. Если вы не используете его, вам просто нужно использовать эту линию фонового изображения для каждого цвета
Дружественный код
36
Обратите внимание, что вы должны urlencode #символа для ваших шестнадцатеричных цветов, чтобы это работало в Firefox. Так что-то вроде <svg fill="#ffffff" ...></svg>становится <svg fill="%23ffffff" ...></svg>.
Swen
1
Сладкий метод. Вы должны жестко закодировать SVG в фоновое изображение, как это? Можете ли вы просто не ссылаться на него как-нибудь?
Люк
2
Я нашел этот сайт полезным для того, чтобы дать вам идеально закодированный URL, готовый к использованию: yoksel.github.io/url-encoder - просто скопируйте в него код SVG и скопируйте возвращенный CSS :-)
Дружественный код
98

Вы можете использовать маски CSS. С помощью свойства 'mask' вы создаете маску, которая применяется к элементу.

.icon {
    background-color: red;
    -webkit-mask-image: url(icon.svg);
    mask-image: url(icon.svg);
}

Для получения дополнительной информации см. Эту замечательную статью: https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images

Адель
источник
3
это хорошо, но при применении к значку в поле ввода весь вводимый текст скрыт.
raison
14
Не поддерживается в IE
Карим
Супер крутой ответ, но вы больше не можете использовать цвет фона при наведении или что-то в этом роде
Пол Крюгер,
Похоже, что это вряд ли поддерживается - слишком много пользователей не смогут увидеть это, чтобы оно было жизнеспособным сегодня в конце 2018 года. Caniuse.com/#feat=css-masks
Крис Москини
3
Лето 2019 года: 94% браузеров на этой планете поддерживают стили «mask-image» ИЛИ «-webkit-mask-image»
Дмитрий Кулахин
57

Еще один подход заключается в использовании маски. Затем вы меняете цвет фона маскируемого элемента. Это имеет тот же эффект, что и изменение атрибута заполнения svg.

HTML:

<glyph class="star"/>
<glyph class="heart" />
<glyph class="heart" style="background-color: green"/>
<glyph class="heart" style="background-color: blue"/>

CSS:

glyph {
    display: inline-block;
    width:  24px;
    height: 24px;
}

glyph.star {
  -webkit-mask: url(star.svg) no-repeat 100% 100%;
  mask: url(star.svg) no-repeat 100% 100%;
  -webkit-mask-size: cover;
  mask-size: cover;
  background-color: yellow;
}

glyph.heart {
  -webkit-mask: url(heart.svg) no-repeat 100% 100%;
  mask: url(heart.svg) no-repeat 100% 100%;
  -webkit-mask-size: cover;
  mask-size: cover;
  background-color: red;
}

Вы найдете полное руководство здесь: http://codepen.io/noahblon/blog/coloring-svgs-in-css-background-images (не мое). Предлагает множество подходов (не ограничиваясь маской).

widged
источник
11
На это следует обратить внимание, это поддержка браузера. Я считаю, что IE (как обычно) сильно отстает в этом.
Мэтью Джонсон
9
К сожалению mask, ни IE, ни Edge не поддерживаются: caniuse.com/#search=mask
alpipego
В Chrome у меня тоже не работает. Изменить: О НВМ ... У меня не включен авторефикс. Я думал, что поставщики должны были прекратить использовать префиксы ?!
mpen
Работает в последнем Chrome и Firefox
тик
16

С Sass это возможно! Единственное, что вам нужно сделать, это кодировать URL-адрес вашего SVG-кода. И это возможно с помощью вспомогательной функции в Sass. Я сделал кодовую ручку для этого. Посмотри на это:

http://codepen.io/philippkuehn/pen/zGEjxB

// choose a color

$icon-color: #F84830;


// functions to urlencode the svg string

@function str-replace($string, $search, $replace: '') {
  $index: str-index($string, $search);
  @if $index {
    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
  }
  @return $string;
}

@function url-encode($string) {
  $map: (
    "%": "%25",
    "<": "%3C",
    ">": "%3E",
    " ": "%20",
    "!": "%21",
    "*": "%2A",
    "'": "%27",
    '"': "%22",
    "(": "%28",
    ")": "%29",
    ";": "%3B",
    ":": "%3A",
    "@": "%40",
    "&": "%26",
    "=": "%3D",
    "+": "%2B",
    "$": "%24",
    ",": "%2C",
    "/": "%2F",
    "?": "%3F",
    "#": "%23",
    "[": "%5B",
    "]": "%5D"
  );
  $new: $string;
  @each $search, $replace in $map {
    $new: str-replace($new, $search, $replace);
  }
  @return $new;
}

@function inline-svg($string) {
  @return url('data:image/svg+xml;utf8,#{url-encode($string)}');
}


// icon styles
// note the fill="' + $icon-color + '"

.icon {
  display: inline-block;
  width: 50px;
  height: 50px;
  background: inline-svg('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
   viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
<path fill="' + $icon-color + '" d="M18.7,10.1c-0.6,0.7-1,1.6-0.9,2.6c0,0.7-0.6,0.8-0.9,0.3c-1.1-2.1-0.4-5.1,0.7-7.2c0.2-0.4,0-0.8-0.5-0.7
  c-5.8,0.8-9,6.4-6.4,12c0.1,0.3-0.2,0.6-0.5,0.5c-0.6-0.3-1.1-0.7-1.6-1.3c-0.2-0.3-0.4-0.5-0.6-0.8c-0.2-0.4-0.7-0.3-0.8,0.3
  c-0.5,2.5,0.3,5.3,2.1,7.1c4.4,4.5,13.9,1.7,13.4-5.1c-0.2-2.9-3.2-4.2-3.3-7.1C19.6,10,19.1,9.6,18.7,10.1z"/>
</svg>');
}
Филипп Кюн
источник
2
Это работает очень хорошо. Единственная проблема: в IE10 значок слишком маленький (я думаю, что 10% от заданного размера.
Хеннинг Фишер,
13
 .icon { 
  width: 48px;
  height: 48px;
  display: inline-block;
  background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/18515/heart.svg) no-repeat 50% 50%; 
  background-size: cover;
}

.icon-orange { 
  -webkit-filter: hue-rotate(40deg) saturate(0.5) brightness(390%) saturate(4); 
  filter: hue-rotate(40deg) saturate(0.5) brightness(390%) saturate(4); 
}

.icon-yellow {
  -webkit-filter: hue-rotate(70deg) saturate(100);
  filter: hue-rotate(70deg) saturate(100);
}

статья и демонстрация Codeben

Эльбаз
источник
1
Этот метод применяет фильтр ко всему объекту, включая детей.
Кшиштоф
9

Загрузите свой svg как текст.

Измените текст SVG с помощью JavaScript, чтобы изменить цвет краски / обводки / заливки [s].

Затем вставьте измененную строку svg inline в ваш css, как описано здесь .

jedierikb
источник
9

Теперь вы можете добиться этого на клиентской стороне следующим образом:

var green = '3CB54A';
var red = 'ED1F24';
var svg = '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"  width="320px" height="100px" viewBox="0 0 320 100" enable-background="new 0 0 320 100" xml:space="preserve"> <polygon class="mystar" fill="#'+green+'" points="134.973,14.204 143.295,31.066 161.903,33.77 148.438,46.896 151.617,65.43 134.973,56.679 118.329,65.43 121.507,46.896 108.042,33.77 126.65,31.066 "/><circle class="mycircle" fill="#'+red+'" cx="202.028" cy="58.342" r="12.26"/></svg>';      
var encoded = window.btoa(svg);
document.body.style.background = "url(data:image/svg+xml;base64,"+encoded+")";

Скрипка здесь!

тнт-Rox
источник
1
Вам следует избегать использования Base64 для SVG, так как это не нужно, увеличивает размер ваших файлов и даже не позволяет GZIP эффективно сжимать эти фрагменты кода.
Инта
1
Эта кодировка происходит на стороне клиента, вероятно, только для полного выхода из
строя
Само собой разумеется, что вы можете сделать что-нибудь с JS. Дело в том, чтобы избежать JS.
MarsAndBack
7

Вы можете сохранить SVG в переменной. Затем управляйте строкой SVG в зависимости от ваших потребностей (например, установите ширину, высоту, цвет и т. Д.). Затем используйте результат для установки фона, например

$circle-icon-svg: '<svg xmlns="http://www.w3.org/2000/svg"><circle cx="10" cy="10" r="10" /></svg>';

$icon-color: #f00;
$icon-color-hover: #00f;

@function str-replace($string, $search, $replace: '') {
    $index: str-index($string, $search);

    @if $index {
        @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
    }

    @return $string;
}

@function svg-fill ($svg, $color) {
  @return str-replace($svg, '<svg', '<svg fill="#{$color}"');
}

@function svg-size ($svg, $width, $height) {
  $svg: str-replace($svg, '<svg', '<svg width="#{$width}"');
  $svg: str-replace($svg, '<svg', '<svg height="#{$height}"');

  @return $svg;
}

.icon {
  $icon-svg: svg-size($circle-icon-svg, 20, 20);

  width: 20px; height: 20px; background: url('data:image/svg+xml;utf8,#{svg-fill($icon-svg, $icon-color)}');

  &:hover {
    background: url('data:image/svg+xml;utf8,#{svg-fill($icon-svg, $icon-color-hover)}');
  }
}

Я тоже сделал демо, http://sassmeister.com/gist/4cf0265c5d0143a9e734 .

Этот код делает несколько предположений о SVG, например, у этого <svg />элемента нет существующего цвета заливки и что не заданы ни свойства ширины, ни высоты. Поскольку входные данные жестко закодированы в документе SCSS, эти ограничения довольно легко реализовать.

Не беспокойтесь о дублировании кода. сжатие делает разницу незначительной.

Gajus
источник
Дублирующий код - это запах кода, поэтому предлагать людям не беспокоиться о дублирующемся коде в случае вашего примера - не очень хорошая идея, однако, тем не менее, сказал, что я не вижу, где ваш код дублируется? Я думаю, что будет лучше, если вы просто удалите комментарий.
профессор программирования
3

Вы можете создать свою собственную функцию SCSS для этого. Добавьте следующее в ваш файл config.rb.

require 'sass'
require 'cgi'

module Sass::Script::Functions

  def inline_svg_image(path, fill)
    real_path = File.join(Compass.configuration.images_path, path.value)
    svg = data(real_path)
    svg.gsub! '{color}', fill.value
    encoded_svg = CGI::escape(svg).gsub('+', '%20')
    data_url = "url('data:image/svg+xml;charset=utf-8," + encoded_svg + "')"
    Sass::Script::String.new(data_url)
  end

private

  def data(real_path)
    if File.readable?(real_path)
      File.open(real_path, "rb") {|io| io.read}
    else
      raise Compass::Error, "File not found or cannot be read: #{real_path}"
    end
  end

end

Тогда вы можете использовать его в своем CSS:

.icon {
  background-image: inline-svg-image('icons/icon.svg', '#555');
}

Вам нужно будет отредактировать SVG-файлы и заменить все атрибуты заливки в разметке на fill = "{color}"

Путь к значку всегда указывается относительно вашего параметра images_dir в том же файле config.rb.

Подобно некоторым другим решениям, но это довольно чисто и сохраняет ваши файлы SCSS аккуратными!

Ломакс
источник
1
это из GitHub-вопроса . Просто
ссылаюсь
2

В некоторых (очень специфических) ситуациях это может быть достигнуто с помощью фильтра . Например, вы можете изменить синее изображение SVG на фиолетовое, повернув оттенок на 45 градусов, используя filter: hue-rotate(45deg);. Поддержка браузера минимальна, но это все еще интересная техника.

демонстрация

Михаэль ван де Верд
источник
2

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

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" preserveAspectRatio="xMidYMid meet" focusable="false" style="pointer-events: none; display: block; width: 100%; height: 100%;" >
    <defs>
        <mask id="Mask">
            <rect width="100%" height="100%" fill="#fff" />
            <polyline stroke-width="2.5" stroke="black" stroke-linecap="square" fill="none" transform="translate(10.373882, 8.762969) rotate(-315.000000) translate(-10.373882, -8.762969) " points="7.99893906 13.9878427 12.7488243 13.9878427 12.7488243 3.53809523"></polyline>
        </mask>
    </defs>
    <rect x="0" y="0" width="20" height="20" fill="white" mask="url(#Mask)" />
</svg>

и чем использовать этот CSS

background-repeat: no-repeat;
background-position: center center;
background-size: contain;
background-image: url(your/path/to.svg);
background-color: var(--color);
user8344212
источник
1

Позже к показу здесь, НО, я смог добавить цвет заливки к многоугольнику SVG, если вы можете напрямую редактировать код SVG, например, следующий svg отображает красный цвет вместо черного по умолчанию. Я не тестировал за пределами Chrome, хотя:

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 width="500px" height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
    <polygon 


        fill="red"


        fill-rule="evenodd" clip-rule="evenodd" points="452.5,233.85 452.5,264.55 110.15,264.2 250.05,390.3 229.3,413.35 
47.5,250.7 229.3,86.7 250.05,109.75 112.5,233.5 "/>
</svg>
Элеонора Циммерманн
источник
-2

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

Мебин Бенни
источник
-5

Много IF, но если ваш SVG в кодировке pre64 запустится:

<svg fill="#000000

Затем начнется закодированная строка base64:

PHN2ZyBmaWxsPSIjMDAwMDAw

если начинается предварительно закодированная строка:

<svg fill="#bfa76e

тогда это кодирует в:

PHN2ZyBmaWxsPSIjYmZhNzZl

Обе закодированные строки начинаются одинаково:

PHN2ZyBmaWxsPSIj

Причудой кодировки base64 является то, что каждые 3 входных символа становятся 4 выходными символами. Если SVG начинается следующим образом, то 6-символьный цвет заливки в шестнадцатеричном формате начинается точно с границы блока кодирования. Поэтому вы можете легко сделать кросс-браузерную замену JS:

output = input.replace(/MDAwMDAw/, "YmZhNzZl");

Но ответ tnt-rox выше - это способ двигаться вперед.

A2D
источник