Modifier la couleur de remplissage SVG lorsqu'elle est utilisée comme image de fond

270

En plaçant la sortie SVG directement en ligne avec le code de la page, je peux simplement modifier les couleurs de remplissage avec CSS comme ceci:

polygon.mystar {
    fill: blue;
}​

circle.mycircle {
    fill: green;
}

Cela fonctionne très bien, mais je cherche un moyen de modifier l'attribut "fill" d'un SVG lorsqu'il est servi en tant qu'IMAGE DE FOND.

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

Comment puis-je changer les couleurs maintenant? Est-ce même possible?

Pour référence, voici le contenu de mon fichier SVG externe:

<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
la source
Je reçois souvent des accessoires pour ma réponse. Vous devriez envisager de la remplacer par la réponse acceptée afin qu'elle ne soit pas manquée.
Ryan

Réponses:

75

Une façon de le faire est de servir votre svg à partir d'un mécanisme côté serveur. Créez simplement un côté serveur de ressources qui génère votre svg en fonction des paramètres GET, et vous le servez sur une certaine URL.

Ensuite, vous utilisez simplement cette URL dans votre CSS.

Parce qu'en tant qu'image de fond, il ne fait pas partie du DOM et vous ne pouvez pas le manipuler. Une autre possibilité serait de l'utiliser régulièrement, de l'intégrer dans une page d'une manière normale, mais de la positionner absolument, de la rendre pleine largeur et hauteur d'une page, puis d'utiliser la propriété z-index css pour la mettre derrière tous les autres éléments DOM sur une page.

tonino.j
la source
7
N'oubliez pas si vous servez le SVG à partir d'un script côté serveur, pour vous assurer que vous envoyez également l'en- tête MIME correct . En PHP, ce serait:<?php header('Content-type: image/svg+xml'); ?>
Saul Fautley
4
Vous pouvez utiliser l'image svg comme masque et manipuler la couleur d'arrière-plan de l'élément. Cela aura le même effet que la modification du remplissage. (réponse détaillée fournie)
élargi
16
Cette réponse était excellente depuis 2012, mais maintenant les masques CSS et / ou les filtres sont pris en charge dans tous les navigateurs depuis un certain temps. Je recommande à tous ceux qui lisent ceci de consulter les liens dans la réponse de widged ci-dessous ou de passer directement aux masques CSS ici , ce qui est une solution très simple - notez qu'il nécessite toujours 2 versions de la règle, une avec le préfixe -webkit- pour le moment temps. Pour Microsoft Edge, les filtres CSS sont actuellement pris en charge, mais pas encore les masques.
joelhardi
2
Il existe de nombreuses solutions ci-dessous qui fonctionnent de nos jours, je suis d'accord que cette réponse ne reflète plus l'état actuel des possibilités.
David Neto
148

J'avais besoin de quelque chose de similaire et je voulais rester avec CSS. Voici les mixins LESS et SCSS ainsi que les CSS simples qui peuvent vous aider. Malheureusement, la prise en charge du navigateur est un peu laxiste. Voir ci-dessous pour plus de détails sur la prise en charge du navigateur.

MOINS mixin:

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

MOINS d'utilisation:

.element-color(#fff);

Mélange SCSS:

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

Utilisation de SCSS:

@include element-color(#fff);

CSS:

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

Voici plus d'informations sur l'intégration du code SVG complet dans votre fichier CSS. Il a également mentionné la compatibilité du navigateur qui est un peu trop petite pour que cela soit une option viable.

Ryan
la source
Bien qu'il y ait un peu de surcharge, car je dois coder en dur tous les éléments svg, je pense que c'est la meilleure solution pendant un certain temps. Merci
Adriano Rosa
Vous devez utiliser un préprocesseur CSS comme Sass ou Less si vous souhaitez utiliser de cette manière. Si vous n'en utilisez pas, il vous suffit d'utiliser cette ligne d'image d'arrière-plan pour chaque couleur
Code convivial
36
Notez que vous devez encoder le #caractère de vos couleurs hexadécimales pour que cela fonctionne dans Firefox. Donc, quelque chose comme <svg fill="#ffffff" ...></svg>devient <svg fill="%23ffffff" ...></svg>.
Swen
1
Méthode douce. Devez-vous coder en dur le svg dans l'image de fond comme ça? Ne pouvez-vous pas simplement vous y connecter d'une manière ou d'une autre?
luke
2
J'ai trouvé ce site utile pour vous donner l'URL parfaitement encodée prête à l'emploi: yoksel.github.io/url-encoder - Copiez simplement le code SVG dedans et copiez le CSS retourné :-)
Code convivial du
98

Vous pouvez utiliser des masques CSS. Avec la propriété 'mask', vous créez un masque qui est appliqué à un élément.

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

Pour plus d'informations, voir cet excellent article: https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images

Adel
la source
3
c'est bien, mais lorsqu'il est appliqué à une icône dans un champ de saisie, tout le texte saisi est masqué.
raison
14
Non pris en charge dans IE
Kareem
Réponse super cool, mais vous ne pouvez plus utiliser la couleur de fond dans un survol ou quelque chose comme ça
Paul Kruger
Cela semble à peine supporté si trop - trop d'utilisateurs ne pourraient pas voir cela pour que cela soit viable aujourd'hui fin 2018. caniuse.com/#feat=css-masks
Chris Moschini
3
Été 2019: 94% des navigateurs de cette planète prennent en charge les styles "mask-image" OR "-webkit-mask-image"
Dmitry Kulahin
57

Une autre approche consiste à utiliser un masque. Vous modifiez ensuite la couleur d'arrière-plan de l'élément masqué. Cela a le même effet que de changer l'attribut fill du 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;
}

Vous trouverez un tutoriel complet ici: http://codepen.io/noahblon/blog/coloring-svgs-in-css-background-images (pas le mien). Il propose une variété d'approches (non limitées au masque).

élargi
la source
11
Une chose à noter à ce sujet est la prise en charge du navigateur. Je pense qu'IE (comme d'habitude) est loin derrière.
Matthew Johnson
9
Malheureusement, maskni pris en charge par IE ni Edge: caniuse.com/#search=mask
alpipego
Ne fonctionne pas non plus pour moi dans Chrome. Edit: Oh nvm ... Je n'ai pas activé le préfixe automatique. Je pensais que les fournisseurs devaient cesser d'utiliser des préfixes?!
mpen
Fonctionne dans les derniers chrome et firefox
tic
16

C'est possible avec Sass! La seule chose que vous avez à faire est de coder en url votre code svg. Et cela est possible avec une fonction d'assistance dans Sass. J'ai fait un codepen pour cela. Regarde ça:

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
la source
2
Cela fonctionne très bien. Seul problème: dans IE10, l'icône est beaucoup trop petite (je pense que 10% de la taille donnée.
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);
}

article et démo de codeben

Elbaz
la source
1
Cette méthode appliquera un filtre à un objet entier, y compris les enfants.
Krzysztof Antoniak
9

Téléchargez votre svg sous forme de texte.

Modifiez votre texte svg en utilisant javascript pour changer la couleur de peinture / trait / remplissage [s].

Ensuite, incorporez la chaîne svg modifiée en ligne dans votre CSS comme décrit ici .

jedierikb
la source
9

Vous pouvez maintenant y parvenir du côté client comme ceci:

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

Jouez ici!

tnt-rox
la source
1
Vous devez éviter d'utiliser Base64 pour SVG car il n'est pas nécessaire, rend vos fichiers plus gros et empêche même GZIP de compresser efficacement ces morceaux de code.
2017
1
Cet encodage se produit du côté client, probablement juste pour un échappement complet ...
diachedelic
Il va sans dire que vous pouvez tout faire avec JS. Le but est d'éviter JS.
MarsAndBack
7

Vous pouvez stocker le SVG dans une variable. Ensuite, manipulez la chaîne SVG en fonction de vos besoins (c'est-à-dire, définissez la largeur, la hauteur, la couleur, etc.). Utilisez ensuite le résultat pour définir l'arrière-plan, par exemple

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

J'ai aussi fait une démo, http://sassmeister.com/gist/4cf0265c5d0143a9e734 .

Ce code fait quelques hypothèses sur le SVG, par exemple que <svg /> élément n'a pas de couleur de remplissage existante et qu'aucune propriété de largeur ou de hauteur n'est définie. Étant donné que l'entrée est codée en dur dans le document SCSS, il est assez facile d'appliquer ces contraintes.

Ne vous inquiétez pas de la duplication de code. la compression rend la différence négligeable.

Gajus
la source
Le code en double est une odeur de code, donc suggérer aux gens de ne pas se soucier du code en double dans le cas de votre exemple n'est pas une bonne idée, mais cela dit, je ne peux pas voir où votre code est dupliqué? Je pense que ce serait mieux si vous supprimiez complètement le commentaire.
Professeur de programmation
3

Pour cela, vous pouvez créer votre propre fonction SCSS. Ajout de ce qui suit à votre fichier 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

Ensuite, vous pouvez l'utiliser dans votre CSS:

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

Vous devrez modifier vos fichiers SVG et remplacer tous les attributs de remplissage du balisage par fill = "{color}"

Le chemin de l'icône est toujours relatif à votre paramètre images_dir dans le même fichier config.rb.

Semblable à certaines des autres solutions, mais c'est assez propre et garde vos fichiers SCSS bien rangés!

Lomax
la source
1
cela vient d'un problème avec github . Il suffit de le référencer ici au cas où quelqu'un voudrait lire la discussion là
MMachinegun
2

Dans certaines situations (très spécifiques), cela peut être réalisé en utilisant un filtre . Par exemple, vous pouvez changer une image SVG bleue en violet en faisant pivoter la teinte de 45 degrés à l'aide de filter: hue-rotate(45deg);. Le support du navigateur est minime mais c'est toujours une technique intéressante.

Démo

Michaël van de Weerd
la source
2

pour l'arrière-plan monochrome, vous pouvez utiliser un svg avec un masque, où la couleur d'arrière-plan doit être affichée

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

et que d'utiliser ce css

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

Tard dans le spectacle ici, MAIS, j'ai pu ajouter une couleur de remplissage au polygone SVG, si vous êtes en mesure de modifier directement le code SVG, donc par exemple le svg suivant s'affiche en rouge, au lieu du noir par défaut. Je n'ai cependant pas testé en dehors 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
la source
-2

C'est ma méthode préférée, mais la prise en charge de votre navigateur doit être très progressive. Avec la propriété mask, vous créez un masque qui est appliqué à un élément. Partout où le masque est opaque ou solide, l'image sous-jacente apparaît à travers. Lorsqu'elle est transparente, l'image sous-jacente est masquée ou masquée. La syntaxe d'une image de masque CSS est similaire à l'image d'arrière-plan. regardez le codepenmask

Mebin Benny
la source
-5

Beaucoup de FI, mais si votre SVG encodé pré base64 démarre:

<svg fill="#000000

Ensuite, la chaîne encodée en base64 démarre:

PHN2ZyBmaWxsPSIjMDAwMDAw

si la chaîne pré-encodée démarre:

<svg fill="#bfa76e

alors cela code pour:

PHN2ZyBmaWxsPSIjYmZhNzZl

Les deux chaînes codées commencent de la même manière:

PHN2ZyBmaWxsPSIj

La particularité de l'encodage base64 est que tous les 3 caractères d'entrée deviennent 4 caractères de sortie. Avec le SVG commençant comme ceci, la couleur de remplissage hexadécimale à 6 caractères commence exactement sur une «frontière» de bloc de codage. Par conséquent, vous pouvez facilement remplacer JS entre plusieurs navigateurs:

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

Mais la réponse tnt-rox ci-dessus est la voie à suivre.

A2D
la source