Zmodyfikuj kolor wypełnienia SVG, gdy jest wyświetlany jako obraz tła

270

Umieszczając wynik SVG bezpośrednio w linii z kodem strony, jestem w stanie po prostu modyfikować kolory wypełnienia za pomocą CSS:

polygon.mystar {
    fill: blue;
}​

circle.mycircle {
    fill: green;
}

Działa to świetnie, jednak szukam sposobu na modyfikację atrybutu „fill” SVG, gdy jest on wyświetlany jako OBRAZ TŁA.

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

Jak mogę teraz zmienić kolory? Czy to w ogóle możliwe?

Dla odniesienia, oto zawartość mojego zewnętrznego pliku 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>
Joe
źródło
Często trafiają mnie rekwizyty za odpowiedź. Powinieneś rozważyć zmianę na akceptowaną odpowiedź, aby nie została pominięta.
Ryan

Odpowiedzi:

75

Jednym ze sposobów na to jest podanie pliku SVG z mechanizmu po stronie serwera. Po prostu stwórz stronę serwera zasobów, która wyprowadza twój plik svg zgodnie z parametrami GET i podajesz go na określonym adresie URL.

Następnie używasz tego adresu URL w swoim css.

Ponieważ jako tło, nie jest częścią DOM i nie można nim manipulować. Inną możliwością byłoby regularne używanie go, osadzanie go na stronie w normalny sposób, ale ustawienie go absolutnie, ustawienie pełnej szerokości i wysokości strony, a następnie użycie właściwości css z-index, aby umieścić go za wszystkimi innymi elementami DOM na stronie.

tonino.j
źródło
7
Nie zapomnij, jeśli podajesz plik SVG ze skryptu po stronie serwera, aby upewnić się, że wysłałeś również prawidłowy nagłówek MIME . W PHP byłoby to:<?php header('Content-type: image/svg+xml'); ?>
Saul Fautley
4
Możesz użyć obrazu svg jako maski i manipulować kolorem tła elementu. Będzie to miało taki sam efekt jak zmiana wypełnienia. (podana szczegółowa odpowiedź)
poszerzono
16
Ta odpowiedź była świetna od 2012 r., Ale teraz maski i / lub filtry CSS są obsługiwane we wszystkich przeglądarkach od pewnego czasu. Polecam każdemu, kto czyta to teraz, przejrzenie linków w odpowiedzi widged poniżej lub po prostu przejdź tutaj do Maski CSS , co jest naprawdę łatwym rozwiązaniem - pamiętaj, że nadal wymaga 2 wersji reguły, jednej z prefiksem -webkit- obecnie czas. W przypadku Microsoft Edge obecnie obsługiwane są filtry CSS, ale jeszcze nie maski.
joelhardi
2
Istnieje wiele rozwiązań, które obecnie działają, zgadzam się, że ta odpowiedź nie odzwierciedla już obecnego stanu możliwości.
David Neto,
148

Potrzebowałem czegoś podobnego i chciałem pozostać przy CSS. Oto miksy LESS i SCSS, a także zwykły CSS, które mogą Ci w tym pomóc. Niestety, obsługa przeglądarki jest nieco luźna. Zobacz poniżej szczegółowe informacje na temat obsługi przeglądarki.

MNIEJ mieszanki:

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

MNIEJ użycia:

.element-color(#fff);

Mieszanka SCSS:

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

Wykorzystanie SCSS:

@include element-color(#fff);

CSS:

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

Oto więcej informacji na temat osadzania pełnego kodu SVG w pliku CSS. Wspomniał także o kompatybilności przeglądarki, która jest nieco za mała, aby była to realna opcja.

Ryan
źródło
Chociaż jest trochę narzutu, ponieważ muszę zakodować wszystkie elementy SVG, myślę, że to najlepsze rozwiązanie na jakiś czas. Dzięki
Adriano Rosa,
Jeśli chcesz używać w ten sposób, musisz użyć preprocesora CSS, takiego jak Sass lub Less. Jeśli nie używasz jednego, musisz użyć linii tła obrazu dla każdego koloru
kod przyjazny
36
Pamiętaj, że musisz urlencode #znak dla kolorów heksadecymalnych, aby działało to w Firefoksie. Coś w rodzaju <svg fill="#ffffff" ...></svg>staje się <svg fill="%23ffffff" ...></svg>.
Swen
1
Słodka metoda. Czy musisz tak mocno zakodować plik SVG w obrazie tła? Czy nie możesz w jakiś sposób po prostu się z tym połączyć?
luke
2
Uznałem, że ta strona jest przydatna, aby dać ci doskonale zakodowany adres URL gotowy do użycia: yoksel.github.io/url-encoder - Po prostu skopiuj do niej kod SVG i skopiuj zwrócony CSS :-)
Przyjazny kod
94

Możesz używać masek CSS. Za pomocą właściwości „mask” możesz utworzyć maskę, która zostanie zastosowana do elementu.

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

Aby uzyskać więcej informacji, zobacz ten wspaniały artykuł: https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images

Adel
źródło
3
jest to dobre, ale po zastosowaniu do ikony w polu wprowadzania cały tekst wejściowy jest ukryty.
raison
14
Nieobsługiwany w IE
Kareem,
Super fajna odpowiedź, ale nie możesz już używać koloru tła w dymku lub czegoś takiego
Paul Kruger
Wydaje się, że jest to ledwo obsługiwane, jeśli w ogóle - zbyt wielu użytkowników nie byłoby w stanie zobaczyć tego, aby było to opłacalne dzisiaj pod koniec 2018 roku. Caniuse.com/#feat=css-masks
Chris Moschini
3
Lato 2019: 94% przeglądarek na tej planecie obsługuje style „mask-image” LUB „-webkit-mask-image”
Dmitry Kulahin
57

Jeszcze innym podejściem jest użycie maski. Następnie zmieniasz kolor tła zamaskowanego elementu. Ma to taki sam efekt, jak zmiana atrybutu wypełnienia pliku 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;
}

Pełny samouczek znajdziesz tutaj: http://codepen.io/noahblon/blog/coloring-svgs-in-css-background-images (nie mój). Proponuje różne podejścia (nie tylko maskowanie).

poszerzony
źródło
11
Należy zwrócić uwagę na obsługę przeglądarki. Uważam, że IE (jak zwykle) jest daleko w tyle.
Matthew Johnson
9
Niestety masknie jest obsługiwany przez IE ani Edge: caniuse.com/#search=mask
alpipego
W Chrome też nie działa. Edycja: Oh nvm ... Nie mam włączonego autoprefixera. Myślałem, że dostawcy powinni przestać używać prefiksów ?!
mpen 30.01.17
Działa w najnowszym chrome i firefox
tic
16

Jest to możliwe dzięki Sass! Jedyne, co musisz zrobić, to zakodować kod svg w adresie URL. Jest to możliwe dzięki funkcji pomocniczej w Sass. Zrobiłem dla tego kodepen. Spójrz na to:

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>');
}
Philipp Kühn
źródło
2
To działa bardzo dobrze. Jedyny problem: w IE10 ikona jest zdecydowanie za mała (myślę, że 10% podanego rozmiaru.
Henning Fischer
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);
}

artykuł codeben i demo

Elbaz
źródło
1
Ta metoda zastosuje filtr do całego obiektu, w tym dzieci.
Krzysztof Antoniak
9

Pobierz plik SVG jako tekst.

Zmodyfikuj tekst SVG za pomocą javascript, aby zmienić kolor farby / obrysu / wypełnienia [s].

Następnie umieść zmodyfikowany ciąg svg bezpośrednio w swoim css, jak opisano tutaj .

jedierikb
źródło
9

Teraz możesz to osiągnąć po stronie klienta w następujący sposób:

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+")";

Baw się tutaj!

tnt-rox
źródło
1
Należy unikać korzystania z Base64 dla SVG, ponieważ jest to niepotrzebne, powiększa pliki, a nawet uniemożliwia GZIP skuteczne kompresowanie tych fragmentów kodu.
inta
1
To kodowanie dzieje się po stronie klienta, prawdopodobnie tylko po to, by całkowicie uciec ...
diachedelic
Jest rzeczą oczywistą, że możesz zrobić wszystko z JS. Chodzi o to, aby uniknąć JS.
MarsAndBack
7

Możesz przechowywać SVG w zmiennej. Następnie manipuluj ciągiem SVG w zależności od potrzeb (tj. Ustaw szerokość, wysokość, kolor itp.). Następnie użyj wyniku, aby ustawić tło, np

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

Zrobiłem też demo, http://sassmeister.com/gist/4cf0265c5d0143a9e734 .

Ten kod zawiera kilka założeń dotyczących SVG, np. Ten <svg />element nie ma istniejącego koloru wypełnienia i że nie są ustawione właściwości szerokości ani wysokości. Ponieważ dane wejściowe są zakodowane na stałe w dokumencie SCSS, dość łatwo jest egzekwować te ograniczenia.

Nie martw się o powielanie kodu. kompresja sprawia, że ​​różnica jest znikoma.

Gajus
źródło
Duplikat kodu to zapach kodu, więc sugerowanie, że ludzie nie powinni się martwić o duplikat kodu w przypadku twojego przykładu, nie jest dobrym pomysłem, ale to powiedziawszy nie widzę, gdzie twój kod jest duplikowany? Myślę, że lepiej by było przeczytać, gdybyś całkowicie usunął komentarz.
Profesor programowania
3

Możesz w tym celu stworzyć własną funkcję SCSS. Dodanie następującego pliku do pliku 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

Następnie możesz użyć go w swoim CSS:

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

Będziesz musiał edytować pliki SVG i zastąpić wszelkie atrybuty wypełnienia w znaczniku fill = "{color}"

Ścieżka ikony jest zawsze względna w stosunku do parametru images_dir w tym samym pliku config.rb.

Podobne do niektórych innych rozwiązań, ale jest to dość czyste i utrzymuje porządek w plikach SCSS!

Lomax
źródło
1
pochodzi z wydania github . Odwołując się tutaj tutaj, na wypadek, gdyby ktoś chciał przeczytać tam dyskusję
MMachinegun,
2

W niektórych (bardzo specyficznych) sytuacjach można to osiągnąć za pomocą filtra . Na przykład możesz zmienić niebieski obraz SVG na fioletowy, obracając odcień o 45 stopni za pomocą filter: hue-rotate(45deg);. Obsługa przeglądarki jest minimalna, ale wciąż jest interesującą techniką.

Próbny

Michaël van de Weerd
źródło
2

dla tła monochromatycznego można użyć svg z maską, w której powinien zostać wyświetlony kolor tła

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

i niż użyj tego css

background-repeat: no-repeat;
background-position: center center;
background-size: contain;
background-image: url(your/path/to.svg);
background-color: var(--color);
użytkownik8344212
źródło
1

Późno na pokaz tutaj, ALE, byłem w stanie dodać kolor wypełnienia do wielokąta SVG, jeśli jesteś w stanie bezpośrednio edytować kod SVG, więc na przykład poniższy svg renderuje czerwony, zamiast domyślnego czarnego. Nie testowałem jednak poza 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>
Eleanor Zimmermann
źródło
-2

To moja ulubiona metoda, ale obsługa przeglądarki musi być bardzo progresywna. Za pomocą właściwości mask tworzysz maskę, która jest stosowana do elementu. Wszędzie, gdzie maska ​​jest nieprzezroczysta lub pełna, widoczny jest obraz pod spodem. Tam, gdzie jest przezroczysty, znajdujący się pod nim obraz jest maskowany lub ukryty. Składnia obrazu maski CSS jest podobna do obrazu tła. spójrz na codepenmask

Mebin Benny
źródło
-5

Wiele IF, ale jeśli uruchomi się Twój SVG zakodowany w standardzie base64:

<svg fill="#000000

Następnie rozpocznie się ciąg zakodowany w base64:

PHN2ZyBmaWxsPSIjMDAwMDAw

jeśli zaczyna się wstępnie zakodowany ciąg:

<svg fill="#bfa76e

następnie koduje to do:

PHN2ZyBmaWxsPSIjYmZhNzZl

Oba zakodowane ciągi zaczynają się tak samo:

PHN2ZyBmaWxsPSIj

Dziwactwo kodowania base64 polega na tym, że co 3 znaki wejściowe stają się 4 znakami wyjściowymi. Gdy SVG zaczyna się w ten sposób, sześcioznakowy kolor wypełnienia heksadecymalnego zaczyna się dokładnie na „granicy” bloku kodowania. Dlatego możesz łatwo wykonać zamianę JS między przeglądarkami:

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

Ale powyższa odpowiedź tnt-rox jest drogą do przodu.

A2D
źródło