Modifique a cor de preenchimento SVG ao ser exibida como imagem de fundo

270

Colocando a saída SVG diretamente alinhada com o código da página, eu posso modificar as cores de preenchimento com CSS da seguinte maneira:

polygon.mystar {
    fill: blue;
}​

circle.mycircle {
    fill: green;
}

Isso funciona muito bem, no entanto, estou procurando uma maneira de modificar o atributo "fill" de um SVG quando ele está sendo servido como uma IMAGEM DE FUNDO.

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

Como posso mudar as cores agora? Isso é possível?

Para referência, aqui está o conteúdo do meu arquivo SVG externo:

<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
fonte
Muitas vezes sou atropelado por adereços para a minha resposta. Você deve alterá-lo para a resposta aceita, para que não perca.
21717 Ryan

Respostas:

75

Uma maneira de fazer isso é servir o seu svg de algum mecanismo do lado do servidor. Simplesmente crie um servidor de recursos que produza seu svg de acordo com os parâmetros GET, e você o serve em um determinado URL.

Então você apenas usa esse URL no seu css.

Porque como img de fundo, ele não faz parte do DOM e você não pode manipulá-lo. Outra possibilidade seria usá-lo regularmente, incorporá-lo em uma página de maneira normal, mas posicioná-lo absolutamente, torná-lo em largura e altura completa de uma página e, em seguida, usar a propriedade z-index css para colocá-lo atrás de todos os outros elementos DOM em uma página.

tonino.j
fonte
7
Não se esqueça de fornecer o SVG a partir de um script do servidor, para garantir o envio do cabeçalho MIME correto . Em PHP, isso seria:<?php header('Content-type: image/svg+xml'); ?>
Saul Fautley
4
Você pode usar a imagem svg como máscara e manipular a cor de fundo do elemento. Isso terá o mesmo efeito que a alteração do preenchimento. (resposta detalhada fornecida)
dia
16
Essa resposta foi ótima a partir de 2012, mas agora as máscaras e / ou filtros CSS são suportados em todos os navegadores há algum tempo. Eu recomendo que qualquer pessoa que esteja lendo isso agora verifique os links na resposta de widged abaixo ou apenas pule para CSS Masks aqui , o que é uma solução realmente fácil - observe que ainda requer 2 versões da regra, uma com o prefixo -webkit- no presente Tempo. Para o Microsoft Edge, atualmente os filtros CSS são suportados, mas ainda não são máscaras.
Joelhardi
2
Existem muitas soluções fornecidas abaixo que funcionam hoje em dia, concordo que essa resposta não reflete mais o estado atual de possibilidades.
David Neto
148

Eu precisava de algo semelhante e queria ficar com o CSS. Aqui estão os mixins LESS e SCSS, bem como CSS simples que podem ajudá-lo com isso. Infelizmente, o suporte ao navegador é um pouco relaxado. Veja abaixo os detalhes sobre o suporte ao navegador.

MENOS mixin:

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

MENOS uso:

.element-color(#fff);

SCSS mixin:

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

Uso de SCSS:

@include element-color(#fff);

CSS:

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

Aqui estão mais informações sobre a incorporação do código SVG completo no seu arquivo CSS. Ele também mencionou a compatibilidade do navegador, que é um pouco pequena para ser uma opção viável.

Ryan
fonte
Embora exista um pouco de sobrecarga, porque eu tenho que codificar todos os itens svg, acho que essa é a melhor solução por um tempo. Obrigado
Adriano Rosa
Você precisa usar um pré-processador CSS como Sass ou Less se quiser usar dessa maneira. Se você não estiver usando um você só precisa usar essa linha background-image para cada cor
Código Amigável
36
Observe que você deve codificar o #caractere das cores hexadecimais para que isso funcione no Firefox. Então, algo como <svg fill="#ffffff" ...></svg>se torna <svg fill="%23ffffff" ...></svg>.
Swen
1
Método doce. Você tem que codificar o svg na imagem de fundo assim? Você não pode simplesmente criar um link para ele de alguma forma?
luke
2
Achei este site útil para fornecer o URL perfeitamente codificado e pronto para uso: yoksel.github.io/url-encoder - Apenas copie o código SVG e copie o CSS retornado :-)
Código Amigável
96

Você pode usar máscaras CSS. Com a propriedade 'mask', você cria uma máscara que é aplicada a um elemento.

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

Para obter mais informações, consulte este excelente artigo: https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images

Adel
fonte
3
isso é bom, mas quando aplicado a um ícone dentro de um campo de entrada, todo o texto de entrada fica oculto.
raison
14
Não suportado no IE
Kareem
Arrefecer Super resposta, mas você não pode mais usar background-color em um passe ou algo parecido
Paul Kruger
Este parece ser mal suportada se em tudo - muitos usuários não seria capaz de ver isso para que seja viável hoje no final de 2018. caniuse.com/#feat=css-masks
Chris Moschini
3
Verão 2019: 94% dos navegadores em este apoio planeta "máscara-imagem" ou "-webkit-mask-imagem" estilos
Dmitry Kulahin
57

Ainda outra abordagem é usar a máscara. Em seguida, você altera a cor do plano de fundo do elemento mascarado. Isso tem o mesmo efeito que alterar o atributo de preenchimento do 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;
}

Você encontrará um tutorial completo aqui: http://codepen.io/noahblon/blog/coloring-svgs-in-css-background-images (não é meu). Ele propõe uma variedade de abordagens (não limitadas à máscara).

viúvo
fonte
11
Uma coisa a se notar é o suporte ao navegador. Eu acredito que o IE (como sempre) está muito atrasado nisso.
Matthew Johnson
9
Infelizmente masknão é nem apoiada por IE nem Borda: caniuse.com/#search=mask
alpipego
Também não está funcionando para mim no Chrome. Edit: Oh nvm ... Eu não tenho o autoprefixer ativado. Eu pensei que os fornecedores deveriam parar de usar prefixos ?!
MPEN
Funciona no mais recente chrome e firefox
tic
16

É possível com Sass! A única coisa que você precisa fazer é codificar em url seu código svg. E isso é possível com uma função auxiliar no Sass. Eu fiz um codepen para isso. Veja isso:

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
fonte
2
Isso funciona muito bem. Apenas problema: No IE10 o ícone é muito pequeno (acho que 10% do determinado tamanho.
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);
}

artigo e demonstração codeben

Elbaz
fonte
1
Este método aplicará o filtro a um objeto inteiro, incluindo filhos.
Krzysztof Antoniak
9

Faça o download do seu svg como texto.

Modifique seu texto svg usando o javascript para alterar as cores [paint] / stroke / fill [s].

Em seguida, incorpore a string svg modificada embutida no seu css, conforme descrito aqui .

jedierikb
fonte
9

Agora você pode conseguir isso no lado do cliente assim:

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

Violino aqui!

tnt-rox
fonte
1
Você deve evitar o uso do Base64 for SVG, pois é desnecessário, aumenta seus arquivos e até impede o GZIP de compactar efetivamente esses blocos de código.
inta
1
Esta codificação está acontecendo no lado do cliente, provavelmente apenas para escapar completa ...
diachedelic
Escusado será dizer que você pode fazer qualquer coisa com JS. O objetivo é evitar o JS.
MarsAndBack 16/01
7

Você pode armazenar o SVG em uma variável. Em seguida, manipule a sequência SVG, dependendo de suas necessidades (por exemplo, defina largura, altura, cor etc.). Em seguida, use o resultado para definir o plano de fundo, por exemplo

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

Também fiz uma demonstração, http://sassmeister.com/gist/4cf0265c5d0143a9e734 .

Este código faz algumas suposições sobre o SVG, por exemplo, esse <svg />elemento não possui uma cor de preenchimento existente e que nem as propriedades de largura nem altura estão definidas. Como a entrada é codificada no documento SCSS, é muito fácil impor essas restrições.

Não se preocupe com a duplicação de código. a compressão torna a diferença insignificante.

Gajus
fonte
Código duplicado é um cheiro de código, portanto, sugerir que as pessoas não devem se preocupar com código duplicado, no caso do seu exemplo, não é uma boa ideia, mas isso disse que eu não consigo ver onde seu código está duplicado? Eu acho que seria melhor ler se você removesse o comentário completamente.
Professor de programação
3

Você pode criar sua própria função SCSS para isso. Adicionando o seguinte ao seu arquivo 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

Então você pode usá-lo em seu CSS:

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

Você precisará editar seus arquivos SVG e substituir quaisquer atributos de preenchimento na marcação por fill = "{color}"

O caminho do ícone é sempre relativo ao seu parâmetro images_dir no mesmo arquivo config.rb.

Semelhante a algumas das outras soluções, mas isso é bastante limpo e mantém seus arquivos SCSS arrumados!

Lomax
fonte
1
isso é de um problema do github . Apenas referenciando-lo aqui no caso de alguém quiser ler a discussão lá
MMachinegun
2

Em algumas situações (muito específicas), isso pode ser alcançado usando um filtro . Por exemplo, você pode alterar uma imagem SVG azul para roxa girando o matiz 45 graus usando filter: hue-rotate(45deg);. O suporte ao navegador é mínimo, mas ainda é uma técnica interessante.

Demo

Michaël van de Weerd
fonte
2

para fundo monocromático, você pode usar um svg com uma máscara, onde a cor do fundo deve ser exibida

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

e que usar esse css

background-repeat: no-repeat;
background-position: center center;
background-size: contain;
background-image: url(your/path/to.svg);
background-color: var(--color);
user8344212
fonte
1

Tarde para o show aqui, MAS, eu era capaz de adicionar uma cor de preenchimento ao polígono SVG, se você conseguir editar diretamente o código SVG, por exemplo, o seguinte svg renderiza vermelho, em vez de preto padrão. Ainda não testei fora do 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
fonte
-2

Este é o meu método favorito, mas o suporte ao seu navegador deve ser muito progressivo. Com a propriedade mask, você cria uma máscara que é aplicada a um elemento. Em todos os lugares em que a máscara é opaca ou sólida, a imagem subjacente é exibida. Onde é transparente, a imagem subjacente é oculta ou oculta. A sintaxe para uma imagem de máscara CSS é semelhante à imagem de fundo. olhe para o codepenmask

Mebin Benny
fonte
-5

Muitos IFs, mas se o seu SVG pré-codificado com base64 iniciar:

<svg fill="#000000

A sequência codificada base64 será iniciada:

PHN2ZyBmaWxsPSIjMDAwMDAw

se a sequência pré-codificada iniciar:

<svg fill="#bfa76e

então isso codifica para:

PHN2ZyBmaWxsPSIjYmZhNzZl

As duas cadeias codificadas começam da mesma maneira:

PHN2ZyBmaWxsPSIj

A peculiaridade da codificação base64 é que cada 3 caracteres de entrada se tornem 4 caracteres de saída. Com o SVG iniciando assim, a cor de preenchimento hexadecimal de 6 caracteres começa exatamente no 'limite' do bloco de codificação. Portanto, você pode facilmente substituir um JS entre navegadores:

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

Mas a resposta tnt-rox acima é o caminho a seguir em frente.

A2D
fonte