Ubah warna isian SVG saat disajikan sebagai Background-Image

270

Menempatkan output SVG secara langsung sejajar dengan kode halaman saya dapat dengan mudah memodifikasi warna isi dengan CSS seperti:

polygon.mystar {
    fill: blue;
}​

circle.mycircle {
    fill: green;
}

Ini berfungsi dengan baik, namun saya sedang mencari cara untuk memodifikasi atribut "isi" dari SVG ketika disajikan sebagai BACKGROUND-IMAGE.

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

Bagaimana saya bisa mengubah warna sekarang? Apakah itu mungkin?

Untuk referensi, berikut adalah isi file SVG eksternal saya:

<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
sumber
Saya sering terpukul dengan alat peraga untuk jawaban saya. Anda harus mempertimbangkan untuk mengubahnya ke jawaban yang diterima sehingga tidak ketinggalan.
Ryan

Jawaban:

75

Salah satu cara untuk melakukan ini adalah melayani svg Anda dari beberapa mekanisme sisi server. Cukup buat sisi server sumber daya yang menampilkan svg Anda sesuai dengan parameter GET, dan Anda menayangkannya di url tertentu.

Maka Anda cukup menggunakan url itu di css Anda.

Karena sebagai latar belakang img, itu bukan bagian dari DOM dan Anda tidak dapat memanipulasi itu. Kemungkinan lain adalah menggunakannya secara teratur, menyematkannya dalam halaman dengan cara normal, tetapi posisikan secara absolut, buat lebar penuh & tinggi halaman, lalu gunakan properti z-index css untuk meletakkannya di belakang semua elemen DOM lainnya di halaman.

tonino.j
sumber
7
Jangan lupa jika Anda menyajikan SVG dari skrip sisi server, untuk memastikan Anda juga mengirim tajuk MIME yang benar . Dalam PHP ini akan menjadi:<?php header('Content-type: image/svg+xml'); ?>
Saul Fautley
4
Anda dapat menggunakan gambar svg sebagai mask dan memanipulasi warna latar elemen. Ini akan memiliki efek yang sama dengan mengubah isi. (jawaban terperinci disediakan)
diperluas
16
Jawaban ini sangat bagus pada 2012, tetapi sekarang masker dan / atau filter CSS telah didukung di semua browser untuk beberapa waktu. Saya menyarankan agar siapa pun yang membaca ini sekarang memeriksa tautan dalam jawaban yang diperluas di bawah ini atau langsung saja ke CSS Masks di sini , yang merupakan solusi yang sangat mudah - perhatikan masih memerlukan 2 versi aturan, satu dengan awalan -webkit- saat ini waktu. Untuk Microsoft Edge, saat ini filter CSS didukung tetapi belum ditutup.
joelhardi
2
Ada banyak solusi yang diberikan di bawah ini yang berfungsi saat ini, saya setuju jawaban ini tidak lagi mencerminkan keadaan kemungkinan saat ini.
David Neto
148

Saya membutuhkan sesuatu yang serupa dan ingin tetap menggunakan CSS. Berikut adalah mixins KURANG dan SCSS serta CSS biasa yang dapat membantu Anda dengan ini. Sayangnya, dukungan browsernya agak longgar. Lihat di bawah untuk detail tentang dukungan browser.

KURANG mixin:

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

KURANG penggunaan:

.element-color(#fff);

SCSS mixin:

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

Penggunaan SCSS:

@include element-color(#fff);

CSS:

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

Ini info lebih lanjut tentang menanamkan kode SVG lengkap ke file CSS Anda. Disebutkan juga kompatibilitas browser yang agak terlalu kecil untuk menjadi opsi yang layak.

Ryan
sumber
Meskipun ada sedikit overhead, karena saya harus melakukan hardcod semua item svg, saya pikir ini adalah solusi terbaik untuk sementara waktu. Terima kasih
Adriano Rosa
Anda perlu menggunakan preprocessor CSS seperti Sass atau Less jika Anda ingin menggunakannya dengan cara ini. Jika Anda tidak menggunakannya, Anda hanya perlu menggunakan garis-gambar latar belakang untuk setiap warna
Kode Ramah
36
Perhatikan bahwa Anda harus urlencode #karakter untuk warna hex Anda untuk membuat ini berfungsi di Firefox. Jadi sesuatu seperti <svg fill="#ffffff" ...></svg>menjadi <svg fill="%23ffffff" ...></svg>.
Swen
1
Metode yang manis Apakah Anda harus menyandikan svg ke dalam gambar latar seperti itu? Tidak bisakah Anda menautkannya ke suatu tempat?
Lukas
2
Saya menemukan situs ini berguna untuk memberi Anda URL yang disandikan sempurna siap digunakan: yoksel.github.io/url-encoder - Cukup salin kode SVG ke dalamnya dan salin CSS yang dikembalikan :-)
Friendly Code
96

Anda dapat menggunakan topeng CSS, Dengan properti 'topeng', Anda membuat topeng yang diterapkan ke suatu elemen.

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

Untuk lebih lanjut lihat artikel hebat ini: https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images

Adel
sumber
3
ini bagus tetapi ketika diterapkan pada ikon dalam bidang input semua teks input disembunyikan.
raison
14
Tidak didukung di IE
Kareem
Jawaban yang sangat keren, tetapi Anda tidak bisa lagi menggunakan warna latar belakang dalam hover atau sesuatu seperti itu
Paul Kruger
Ini tampaknya hampir tidak didukung jika sama sekali - terlalu banyak pengguna tidak akan dapat melihat ini untuk itu menjadi layak hari ini di akhir 2018. caniuse.com/#feat=css-masks
Chris Moschini
3
Musim panas 2019: 94% browser di planet ini mendukung gaya "topeng-gambar" ATAU "-webkit-topeng-gambar"
Dmitry Kulahin
57

Namun pendekatan lain adalah menggunakan topeng. Anda kemudian mengubah warna latar belakang elemen yang disembunyikan. Ini memiliki efek yang sama dengan mengubah atribut isian 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;
}

Anda akan menemukan tutorial lengkap di sini: http://codepen.io/noahblon/blog/coloring-svgs-in-css-background-images (bukan milik saya). Ini mengusulkan berbagai pendekatan (tidak terbatas pada topeng).

melebar
sumber
11
Satu hal yang perlu diperhatikan dalam hal ini adalah dukungan browser. Saya percaya IE (seperti biasa) jauh di belakang dalam hal ini.
Matthew Johnson
9
Sayangnya maskyang tidak didukung oleh IE atau Ujung: caniuse.com/#search=mask
alpipego
Saya juga tidak bekerja di Chrome. Sunting: Oh nvm ... Saya tidak mengaktifkan autoprefixer. Saya pikir vendor seharusnya berhenti menggunakan awalan ?!
mpen
Bekerja dalam chrome terbaru dan firefox
tic
16

Itu mungkin dengan Sass! Satu-satunya hal yang harus Anda lakukan adalah url-encode kode svg Anda. Dan ini dimungkinkan dengan fungsi pembantu di Sass. Saya sudah membuat codepen untuk ini. Lihat ini:

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
sumber
2
Ini bekerja sangat baik. Satu-satunya masalah: Di IE10 ikonnya terlalu kecil (saya pikir 10% dari ukuran yang diberikan.
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);
}

artikel codeben dan demo

Elbaz
sumber
1
Metode ini akan menerapkan filter ke seluruh objek, termasuk anak-anak.
Krzysztof Antoniak
9

Unduh svg Anda sebagai teks.

Ubah teks svg Anda menggunakan javascript untuk mengubah warna cat / stroke / fill [s].

Kemudian masukkan string svg yang dimodifikasi ke dalam css Anda seperti dijelaskan di sini .

jedierikb
sumber
9

Sekarang Anda dapat mencapai ini di sisi klien seperti ini:

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

Biola di sini!

tnt-rox
sumber
1
Anda harus menghindari penggunaan Base64 untuk SVG karena tidak perlu, membuat file Anda lebih besar dan bahkan mencegah GZIP memampatkan potongan kode tersebut secara efektif.
Inta
1
Pengkodean ini terjadi di sisi klien, mungkin hanya untuk melarikan diri secara menyeluruh ...
diachedelic
Tak usah dikatakan Anda dapat melakukan apa pun dengan JS. Intinya adalah untuk menghindari JS.
MarsAndBack
7

Anda dapat menyimpan SVG dalam sebuah variabel. Kemudian memanipulasi string SVG tergantung pada kebutuhan Anda (yaitu, mengatur lebar, tinggi, warna, dll). Kemudian gunakan hasilnya untuk mengatur latar belakang, mis

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

Saya telah membuat demo juga, http://sassmeister.com/gist/4cf0265c5d0143a9e734 .

Kode ini membuat beberapa asumsi tentang SVG, misalnya <svg />elemen yang tidak memiliki warna isian yang ada dan bahwa properti lebar atau tinggi tidak diatur. Karena input di-hardcode dalam dokumen SCSS, cukup mudah untuk menegakkan batasan-batasan ini.

Jangan khawatir tentang duplikasi kode. kompresi membuat perbedaan dapat diabaikan.

Gajus
sumber
Kode duplikat adalah bau kode sehingga menyarankan orang tidak perlu khawatir tentang kode duplikat dalam kasus contoh Anda bukan ide yang baik, namun itu mengatakan saya tidak bisa melihat di mana kode Anda digandakan? Saya pikir ini akan lebih baik jika Anda menghapus komentar sama sekali.
Profesor pemrograman
3

Anda dapat membuat fungsi SCSS Anda sendiri untuk ini. Menambahkan berikut ini ke file config.rb Anda.

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

Kemudian Anda dapat menggunakannya di CSS Anda:

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

Anda perlu mengedit file SVG Anda dan mengganti atribut isian apa pun di markup dengan fill = "{color}"

Ikon path selalu relatif terhadap parameter images_dir Anda dalam file config.rb yang sama.

Mirip dengan beberapa solusi lain, tetapi ini cukup bersih dan membuat file SCSS Anda rapi!

Lomax
sumber
1
ini dari masalah github . Referensi saja di sini kalau-kalau ada yang ingin membaca diskusi di sana
MMachinegun
2

Dalam beberapa situasi (sangat spesifik) ini dapat dicapai dengan menggunakan filter . Misalnya, Anda dapat mengubah gambar SVG biru menjadi ungu dengan memutar hue 45 derajat menggunakan filter: hue-rotate(45deg);. Dukungan peramban sangat minim tetapi masih merupakan teknik yang menarik.

Demo

Michaël van de Weerd
sumber
2

untuk latar belakang monokrom Anda dapat menggunakan svg dengan topeng, di mana warna latar belakang harus ditampilkan

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

dan daripada menggunakan css ini

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

Terlambat acara di sini, TETAPI, saya bisa menambahkan warna isian ke poligon SVG, jika Anda dapat langsung mengedit kode SVG, jadi misalnya svg berikut membuat merah, bukan hitam standar. Saya belum menguji di luar 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
sumber
-2

Ini adalah metode favorit saya, tetapi dukungan browser Anda harus sangat progresif. Dengan properti mask, Anda membuat topeng yang diterapkan ke elemen. Di mana-mana topeng itu buram, atau padat, gambar di bawahnya terlihat. Jika transparan, gambar yang mendasarinya disembunyikan, atau disembunyikan. Sintaks untuk gambar topeng CSS mirip dengan gambar latar. lihat codepen itumask

Mebin Benny
sumber
-5

Banyak IF, tetapi jika SVG pra-encode pra base64 Anda mulai:

<svg fill="#000000

Kemudian string yang disandikan base64 akan mulai:

PHN2ZyBmaWxsPSIjMDAwMDAw

jika string pra-enkode dimulai:

<svg fill="#bfa76e

maka ini disandikan ke:

PHN2ZyBmaWxsPSIjYmZhNzZl

Kedua string yang disandikan mulai sama:

PHN2ZyBmaWxsPSIj

Keunikan dari encoding base64 adalah setiap 3 karakter input menjadi 4 karakter output. Dengan SVG dimulai seperti ini maka warna hex mengisi 6 karakter dimulai tepat pada 'batas' blok penyandian. Karena itu Anda dapat dengan mudah melakukan penggantian lintas-browser JS:

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

Tetapi jawaban di atas adalah cara untuk terus maju.

A2D
sumber