Modifique el color de relleno SVG cuando se sirva como imagen de fondo

270

Al colocar la salida SVG directamente en línea con el código de página, puedo modificar simplemente los colores de relleno con CSS de la siguiente manera:

polygon.mystar {
    fill: blue;
}​

circle.mycircle {
    fill: green;
}

Esto funciona muy bien, sin embargo, estoy buscando una manera de modificar el atributo de "relleno" de un SVG cuando se sirve como una IMAGEN DE FONDO.

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

¿Cómo puedo cambiar los colores ahora? ¿Es posible?

Como referencia, aquí están los contenidos de mi archivo 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
fuente
A menudo me golpean con accesorios para mi respuesta. Debería considerar cambiarlo a la respuesta aceptada para que no se lo pierda.
Ryan

Respuestas:

75

Una forma de hacerlo es servir su svg desde algún mecanismo del lado del servidor. Simplemente cree un lado del servidor de recursos que genere su svg de acuerdo con los parámetros GET, y lo servirá en una determinada URL.

Entonces solo usas esa url en tu CSS.

Porque como imagen de fondo, no es parte del DOM y no puedes manipularlo. Otra posibilidad sería usarlo regularmente, incrustarlo en una página de una manera normal, pero posicionarlo absolutamente, hacerlo de ancho y alto completo de una página y luego usar la propiedad z-index css para colocarlo detrás de todos los demás elementos DOM en una página

tonino.j
fuente
77
No olvide si está sirviendo el SVG desde un script del lado del servidor, para asegurarse de que también envíe el encabezado MIME correcto . En PHP esto sería:<?php header('Content-type: image/svg+xml'); ?>
Saul Fautley
44
Puede usar la imagen svg como máscara y manipular el color de fondo del elemento. Esto tendrá el mismo efecto que cambiar el relleno. (se proporciona respuesta detallada)
widged
16
Esta respuesta fue excelente a partir de 2012, pero ahora se han admitido máscaras y / o filtros CSS en todos los navegadores durante algún tiempo. Recomiendo que cualquiera que lea esto ahora revise los enlaces en la respuesta de widged a continuación o simplemente salte a Máscaras CSS aquí , que es una solución realmente fácil: tenga en cuenta que todavía requiere 2 versiones de la regla, una con el prefijo -webkit- en la actualidad hora. Para Microsoft Edge, actualmente los filtros CSS son compatibles pero aún no son máscaras.
joelhardi el
2
Hay muchas soluciones a continuación que funcionan hoy en día, estoy de acuerdo en que esta respuesta ya no refleja el estado actual de posibilidades.
David Neto
148

Necesitaba algo similar y quería seguir con CSS. Aquí hay combinaciones de MENOS y SCSS, así como CSS simple que pueden ayudarlo con esto. Desafortunadamente, el soporte del navegador es un poco laxo. Consulte a continuación para obtener detalles sobre la compatibilidad del navegador.

MENOS mezcla

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

Aquí hay más información sobre cómo incrustar el código SVG completo en su archivo CSS. También mencionó la compatibilidad del navegador, que es demasiado pequeña para que esta sea una opción viable.

Ryan
fuente
Aunque hay un poco de sobrecarga, porque tengo que codificar todos los elementos svg, creo que esta es la mejor solución por un tiempo. Gracias
Adriano Rosa
Debe usar un preprocesador CSS como Sass o Less si quiere usarlo de esta manera. Si no está usando uno, solo necesitará usar esa línea de imagen de fondo para cada color
Código
36
Tenga en cuenta que debe codificar el #carácter de sus colores hexadecimales para que esto funcione en Firefox. Entonces algo como se <svg fill="#ffffff" ...></svg>convierte <svg fill="%23ffffff" ...></svg>.
Swen
1
Método dulce ¿Tiene que codificar el svg en la imagen de fondo de esa manera? ¿No puedes simplemente vincularlo de alguna manera?
luke
2
Encontré este sitio útil para darle la URL perfectamente codificada lista para usar: yoksel.github.io/url-encoder - Simplemente copie el código SVG y copie el CSS devuelto :-)
Código
98

Puede usar máscaras CSS. Con la propiedad 'máscara', puede crear una máscara que se aplica a un elemento.

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

Para obtener más información, vea este excelente artículo: https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images

Adel
fuente
3
Esto es bueno, pero cuando se aplica a un icono dentro de un campo de entrada, todo el texto de entrada está oculto.
Raison
14
No es compatible con IE
Kareem
Respuesta genial, pero ya no puedes usar el color de fondo en un vuelo estacionario o algo así
Paul Kruger
Parece que esto apenas es compatible, ya que muchos usuarios no podrían ver esto para que sea viable hoy a fines de 2018. caniuse.com/#feat=css-masks
Chris Moschini
3
Verano de 2019: el 94% de los navegadores en este planeta admiten estilos de "máscara-imagen" O "-webkit-mask-image"
Dmitry Kulahin
57

Otro enfoque es usar la máscara. Luego cambia el color de fondo del elemento enmascarado. Esto tiene el mismo efecto que cambiar el atributo de relleno de la 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;
}

Encontrará un tutorial completo aquí: http://codepen.io/noahblon/blog/coloring-svgs-in-css-background-images (no el mío). Propone una variedad de enfoques (no limitados a la máscara).

widged
fuente
11
Una cosa a tener en cuenta en esto es el soporte del navegador. Creo que IE (como siempre) está muy atrasado en esto.
Matthew Johnson el
9
Lamentablemente, maskni IE ni Edge admiten
alpipego
No funciona para mí en Chrome tampoco. Editar: Oh nvm ... No tengo habilitado el autoprefixer. ¡¿Pensé que se suponía que los vendedores dejarían de usar prefijos ?!
mpen
Funciona en el último Chrome y Firefox
tic
16

¡Es posible con Sass! Lo único que tienes que hacer es codificar en url tu código svg. Y esto es posible con una función auxiliar en Sass. He hecho un codepen para esto. Mira este:

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
fuente
2
Esto funciona muy bien El único problema: en IE10, el icono es demasiado pequeño (creo que el 10% del tamaño dado.)
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);
}

artículo y demostración de codeben

Elbaz
fuente
1
Este método aplicará filtro a un objeto completo, incluidos los hijos.
Krzysztof Antoniak
9

Descargue su svg como texto.

Modifique su texto svg usando javascript para cambiar los colores de pintura / trazo / relleno.

Luego incruste la cadena svg modificada en línea en su CSS como se describe aquí .

jedierikb
fuente
9

Ahora puede lograr esto en el lado del cliente de esta manera:

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

¡Violín aquí!

tnt-rox
fuente
1
Debe evitar el uso de Base64 para SVG ya que es innecesario, hace que sus archivos sean más grandes e incluso evita que GZIP comprima efectivamente esos fragmentos de código.
inta
1
Esta codificación está sucediendo en el lado del cliente, probablemente sólo para escapar a fondo ...
diachedelic
No hace falta decir que puedes hacer algo con JS. El punto es evitar JS.
MarsAndBack
7

Puede almacenar el SVG en una variable. Luego manipule la cadena SVG según sus necesidades (es decir, ajuste el ancho, la altura, el color, etc.). Luego use el resultado para establecer el fondo, por ej.

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

También hice una demostración, http://sassmeister.com/gist/4cf0265c5d0143a9e734 .

Este código hace algunas suposiciones sobre el SVG, por ejemplo, ese <svg />elemento no tiene un color de relleno existente y que no se establecen propiedades de ancho o alto. Dado que la entrada está codificada en el documento SCSS, es bastante fácil hacer cumplir estas restricciones.

No te preocupes por la duplicación de código. la compresión hace que la diferencia sea insignificante.

Gajus
fuente
El código duplicado es un olor a código, por lo que sugerir a las personas que no se preocupen por el código duplicado en el caso de su ejemplo no es una buena idea, sin embargo, ¿eso dijo que no puedo ver dónde está duplicado su código? Creo que sería mejor leer si eliminaras el comentario por completo.
Profesor de programación el
3

Puede crear su propia función SCSS para esto. Agregar lo siguiente a su archivo 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

Entonces puedes usarlo en tu CSS:

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

Deberá editar sus archivos SVG y reemplazar cualquier atributo de relleno en el marcado con fill = "{color}"

La ruta del icono siempre es relativa a su parámetro images_dir en el mismo archivo config.rb.

Similar a algunas de las otras soluciones, ¡pero esto es bastante limpio y mantiene sus archivos SCSS ordenados!

Lomax
fuente
1
Esto es de un problema de Github . Simplemente haciendo referencia aquí en caso de que alguien quiera leer la discusión allí
MMachinegun
2

En algunas situaciones (muy específicas) esto podría lograrse mediante el uso de un filtro . Por ejemplo, puede cambiar una imagen SVG azul a púrpura girando el tono 45 grados usando filter: hue-rotate(45deg);. El soporte del navegador es mínimo, pero sigue siendo una técnica interesante.

Manifestación

Michaël van de Weerd
fuente
2

para el fondo monocromo, puede usar un svg con una máscara, donde se debe mostrar el color de fondo

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

y que use este css

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

Tarde al show aquí, PERO, pude agregar un color de relleno al polígono SVG, si puede editar directamente el código SVG, por ejemplo, el siguiente svg se vuelve rojo, en lugar del negro predeterminado. Sin embargo, no he probado fuera de 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
fuente
-2

Este es mi método favorito, pero el soporte de su navegador debe ser muy progresivo. Con la propiedad de máscara, crea una máscara que se aplica a un elemento. En todas partes la máscara es opaca o sólida, la imagen subyacente se muestra a través. Donde es transparente, la imagen subyacente se oculta u oculta. La sintaxis para una imagen de máscara CSS es similar a la imagen de fondo. mira el codepenmask

Mebin Benny
fuente
-5

Muchos IF, pero si comienza su SVG codificado pre base64:

<svg fill="#000000

Entonces la cadena codificada en base64 comenzará:

PHN2ZyBmaWxsPSIjMDAwMDAw

si comienza la cadena precodificada:

<svg fill="#bfa76e

entonces esto codifica para:

PHN2ZyBmaWxsPSIjYmZhNzZl

Ambas cadenas codificadas comienzan igual:

PHN2ZyBmaWxsPSIj

La peculiaridad de la codificación base64 es que cada 3 caracteres de entrada se convierten en 4 caracteres de salida. Con el SVG comenzando así, el color de relleno hexadecimal de 6 caracteres comienza exactamente en un 'límite' de bloque de codificación. Por lo tanto, puede hacer fácilmente un reemplazo JS entre navegadores:

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

Pero la respuesta anterior de tnt-rox es el camino a seguir para avanzar.

A2D
fuente