Pertanyaan saya adalah: diberi warna RGB target, apa rumus untuk mewarnai ulang hitam ( #000
) menjadi warna itu hanya dengan menggunakan filter CSS ?
Agar jawaban dapat diterima, perlu menyediakan fungsi (dalam bahasa apa pun) yang akan menerima warna target sebagai argumen dan mengembalikan filter
string CSS yang sesuai .
Konteksnya adalah kebutuhan untuk mewarnai ulang SVG di dalam a background-image
. Dalam hal ini, ini untuk mendukung fitur matematika TeX tertentu di KaTeX: https://github.com/Khan/KaTeX/issues/587 .
Contoh
Jika warna target adalah #ffff00
(kuning), solusi yang tepat adalah:
filter: invert(100%) sepia() saturate(10000%) hue-rotate(0deg)
( demo )
Bukan tujuan
- Animasi.
- Solusi non-CSS-filter.
- Mulai dari warna selain hitam.
- Peduli tentang apa yang terjadi pada warna selain hitam.
Hasil sejauh ini
Pencarian paksa untuk parameter dari daftar filter tetap: https://stackoverflow.com/a/43959856/181228
Kekurangan: tidak efisien, hanya menghasilkan beberapa dari 16.777.216 kemungkinan warna (676.248 denganhueRotateStep=1
).Solusi pencarian yang lebih cepat menggunakan SPSA : https://stackoverflow.com/a/43960991/181228 Bounty diberikan
Sebuah
drop-shadow
solusi: https://stackoverflow.com/a/43959853/181228
Cons: Apakah tidak bekerja pada Edge. Memerlukanfilter
perubahan non- CSS dan sedikit perubahan HTML.
Anda masih bisa mendapatkan jawaban Diterima dengan mengirimkan solusi non brute force!
Sumber daya
Bagaimana
hue-rotate
dansepia
dihitung: https://stackoverflow.com/a/29521147/181228 Contoh implementasi Ruby:LUM_R = 0.2126; LUM_G = 0.7152; LUM_B = 0.0722 HUE_R = 0.1430; HUE_G = 0.1400; HUE_B = 0.2830 def clamp(num) [0, [255, num].min].max.round end def hue_rotate(r, g, b, angle) angle = (angle % 360 + 360) % 360 cos = Math.cos(angle * Math::PI / 180) sin = Math.sin(angle * Math::PI / 180) [clamp( r * ( LUM_R + (1 - LUM_R) * cos - LUM_R * sin ) + g * ( LUM_G - LUM_G * cos - LUM_G * sin ) + b * ( LUM_B - LUM_B * cos + (1 - LUM_B) * sin )), clamp( r * ( LUM_R - LUM_R * cos + HUE_R * sin ) + g * ( LUM_G + (1 - LUM_G) * cos + HUE_G * sin ) + b * ( LUM_B - LUM_B * cos - HUE_B * sin )), clamp( r * ( LUM_R - LUM_R * cos - (1 - LUM_R) * sin ) + g * ( LUM_G - LUM_G * cos + LUM_G * sin ) + b * ( LUM_B + (1 - LUM_B) * cos + LUM_B * sin ))] end def sepia(r, g, b) [r * 0.393 + g * 0.769 + b * 0.189, r * 0.349 + g * 0.686 + b * 0.168, r * 0.272 + g * 0.534 + b * 0.131] end
Perhatikan bahwa di
clamp
atas membuathue-rotate
fungsi menjadi non-linier.Demo: Mendapatkan warna non-skala abu-abu dari warna abu-abu: https://stackoverflow.com/a/25524145/181228
Formula yang hampir berfungsi (dari pertanyaan serupa ):
https://stackoverflow.com/a/29958459/181228Penjelasan mendetail mengapa rumus di atas salah (CSS
hue-rotate
bukanlah rotasi hue yang sebenarnya, melainkan perkiraan linier):
https://stackoverflow.com/a/19325417/2441511
sumber
Jawaban:
@Dave adalah orang pertama yang memposting jawaban untuk ini (dengan kode yang berfungsi), dan jawabannya telah menjadi sumber inspirasi
tempel dan salinan tak tahu malubagi saya. Postingan ini dimulai sebagai upaya untuk menjelaskan dan menyempurnakan jawaban @ Dave, tetapi sejak itu berkembang menjadi jawabannya sendiri.Metode saya jauh lebih cepat. Menurut tolok ukur jsPerf pada warna RGB yang dihasilkan secara acak, algoritme @ Dave berjalan dalam 600 ms , sedangkan milikku berjalan dalam 30 ms . Ini pasti bisa menjadi masalah, misalnya dalam waktu muat, di mana kecepatan sangat penting.
Selain itu, untuk beberapa warna, algoritme saya bekerja lebih baik:
rgb(0,255,0)
, produksirgb(29,218,34)
dan produksi @ Davergb(1,255,0)
rgb(0,0,255)
, produksi @ Davergb(37,39,255)
dan produksi sayargb(5,6,255)
rgb(19,11,118)
, produksi @ Davergb(36,27,102)
dan produksi sayargb(20,11,112)
Demo
Pemakaian
Penjelasan
Kami akan mulai dengan menulis beberapa Javascript.
Penjelasan:
Color
kelas merupakan warna RGB.toString()
mengembalikan warna dalamrgb(...)
string warna CSS .hsl()
mengembalikan warna, diubah menjadi HSL .clamp()
memastikan bahwa nilai warna tertentu berada dalam batas (0-255).Solver
kelas akan mencoba untuk memecahkan warna sasaran.css()
mengembalikan filter yang diberikan dalam string filter CSS.Menerapkan
grayscale()
,sepia()
dansaturate()
Inti dari filter CSS / SVG adalah filter primitif , yang mewakili modifikasi tingkat rendah pada gambar.
Filter
grayscale()
,,sepia()
dansaturate()
diimplementasikan oleh filter primatif<feColorMatrix>
, yang melakukan perkalian matriks antara matriks yang ditentukan oleh filter (sering dibuat secara dinamis), dan matriks yang dibuat dari warna. Diagram:Ada beberapa pengoptimalan yang bisa kami lakukan di sini:
1
. Tidak ada gunanya menghitung atau menyimpannya.A
), karena kita berurusan dengan RGB, bukan RGBA.<feColorMatrix>
filter meninggalkan kolom 4 dan 5 sebagai nol. Oleh karena itu, kami selanjutnya dapat mengurangi matriks filter menjadi 3x3 .Penerapan:
(Kami menggunakan variabel sementara untuk menampung hasil perkalian setiap baris, karena kami tidak ingin perubahan
this.r
, dll. Mempengaruhi perhitungan selanjutnya.)Sekarang kita telah menerapkan
<feColorMatrix>
, kita dapat menerapkangrayscale()
,sepia()
, dansaturate()
, yang hanya meminta dengan matriks filter yang diberikan:Menerapkan
hue-rotate()
The
hue-rotate()
filter dilaksanakan oleh<feColorMatrix type="hueRotate" />
.Matriks filter dihitung seperti yang ditunjukkan di bawah ini:
Misalnya, elemen yang 00 akan dihitung seperti:
Beberapa catatan:
Math.sin()
atauMath.cos()
.Math.sin(angle)
danMath.cos(angle)
harus dihitung sekali dan kemudian disimpan dalam cache.Penerapan:
Menerapkan
brightness()
dancontrast()
The
brightness()
dancontrast()
filter dilaksanakan oleh<feComponentTransfer>
dengan<feFuncX type="linear" />
.Setiap
<feFuncX type="linear" />
elemen menerima atribut kemiringan dan intersep . Kemudian menghitung setiap nilai warna baru melalui rumus sederhana:Ini mudah diterapkan:
Setelah ini diterapkan,
brightness()
dancontrast()
dapat diterapkan juga:Menerapkan
invert()
The
invert()
filter dilaksanakan oleh<feComponentTransfer>
dengan<feFuncX type="table" />
.Spesifikasi menyatakan:
Penjelasan rumus ini:
invert()
Filter mendefinisikan tabel ini: [value, 1 - value]. Ini adalah tableValues atau v .Dengan demikian, kita dapat menyederhanakan rumusnya menjadi:
Menyebariskan nilai tabel, kita mendapatkan:
Satu lagi penyederhanaan:
Spesifikasi mendefinisikan C dan C ' menjadi nilai RGB, dalam batas 0-1 (sebagai lawan 0-255). Akibatnya, kita harus menurunkan nilai sebelum komputasi, dan menskalakannya kembali setelahnya.
Jadi kami sampai pada implementasi kami:
Selingan: Algoritma kekerasan @ Dave
Kode @ Dave menghasilkan 176.660 kombinasi filter, termasuk:
invert()
filter (0%, 10%, 20%, ..., 100%)sepia()
filter (0%, 10%, 20%, ..., 100%)saturate()
filter (5%, 10%, 15%, ..., 100%)hue-rotate()
filter (0deg, 5deg, 10deg, ..., 360deg)Ini menghitung filter dalam urutan berikut:
Kemudian iterasi melalui semua warna yang dihitung. Ini berhenti setelah menemukan warna yang dihasilkan dalam toleransi (semua nilai RGB berada dalam 5 unit dari warna target).
Namun, ini lambat dan tidak efisien. Demikian jawaban saya sendiri.
Menerapkan SPSA
Pertama, kita harus mendefinisikan fungsi kerugian , yang mengembalikan perbedaan antara warna yang dihasilkan oleh kombinasi filter, dan warna target. Jika filternya sempurna, fungsi kerugian harus mengembalikan 0.
Kami akan mengukur perbedaan warna sebagai jumlah dari dua metrik:
hue-rotate()
, saturasi berkorelasi dengansaturate()
, dll.) Ini memandu algoritme.Fungsi kerugian akan mengambil satu argumen - larik persentase filter.
Kami akan menggunakan urutan filter berikut:
Penerapan:
Kami akan mencoba meminimalkan fungsi kerugian, sehingga:
The SPSA algoritma ( situs , info lebih lanjut , kertas , kertas implementasi , kode referensi ) sangat pandai dalam hal ini. Ini dirancang untuk mengoptimalkan sistem yang kompleks dengan fungsi minima lokal, noise / nonlinear / multivariate loss, dll. Telah digunakan untuk menyetel mesin catur . Dan tidak seperti banyak algoritme lainnya, makalah yang mendeskripsikannya sebenarnya dapat dipahami (meskipun dengan usaha keras).
Penerapan:
Saya membuat beberapa modifikasi / pengoptimalan pada SPSA:
deltas
,highArgs
,lowArgs
), bukannya menciptakan mereka dengan setiap iterasi.fix
fungsi setelah setiap iterasi. Ini menjepit semua nilai antara 0% dan 100%, kecualisaturate
(di mana maksimum adalah 7500%),brightness
dancontrast
(di mana maksimum adalah 200%), danhueRotate
(di mana nilai-nilai dibungkus bukan dijepit).Saya menggunakan SPSA dalam proses dua tahap:
Penerapan:
Menyetel SPSA
Peringatan: Jangan main-main dengan kode SPSA, terutama dengan konstanta, kecuali Anda yakin tahu apa yang Anda lakukan.
Konstanta yang penting adalah A , a , c , nilai awal, retry threshold, nilai
max
infix()
, dan jumlah iterasi setiap tahapan. Semua nilai ini disetel dengan cermat untuk menghasilkan hasil yang baik, dan mengacaukannya secara acak hampir pasti akan mengurangi kegunaan algoritme.Jika Anda bersikeras untuk mengubahnya, Anda harus mengukurnya sebelum Anda "mengoptimalkan".
Pertama, terapkan tambalan ini .
Kemudian jalankan kode di Node.js. Setelah beberapa lama, hasilnya akan seperti ini:
Sekarang setel konstanta sesuka hati Anda.
Beberapa tips:
--debug
bendera jika Anda ingin melihat hasil dari setiap iterasi.TL; DR
sumber
<filter>
berisi a<feColorMatrix>
dengan nilai yang tepat (semua nol kecuali kolom terakhir, yang berisi RGB target values, 0, dan 1), masukkan SVG ke DOM, dan referensikan filter dari CSS. Silakan tulis solusi Anda sebagai jawaban (dengan demo), dan saya akan memberi suara positif.Ini adalah perjalanan yang cukup menyusuri lubang kelinci tapi ini dia!
EDIT: Solusi ini tidak dimaksudkan untuk penggunaan produksi dan hanya menggambarkan pendekatan yang dapat diambil untuk mencapai apa yang diminta OP. Sebenarnya, itu lemah di beberapa area spektrum warna. Hasil yang lebih baik dapat dicapai dengan lebih banyak perincian dalam iterasi langkah atau dengan menerapkan lebih banyak fungsi filter untuk alasan yang dijelaskan secara mendetail dalam jawaban @ MultiplyByZer0 .
EDIT2: OP sedang mencari solusi non brute force. Dalam hal ini cukup sederhana, selesaikan saja persamaan ini:
dimana
sumber
255,0,255
, pengukur warna digital saya melaporkan hasilnya sebagai#d619d9
gantinya#ff00ff
.clamp
?Catatan: OP meminta saya untuk membatalkan penghapusan , tetapi hadiah akan diberikan ke jawaban Dave.
Saya tahu ini bukan yang ditanyakan di badan pertanyaan, dan tentu saja bukan yang kita semua tunggu, tetapi ada satu filter CSS yang melakukan persis seperti ini:
drop-shadow()
Peringatan:
sumber
background-color: black;
untuk.icon>span
membuat ini bekerja untuk FF 69b. Namun, tidak menunjukkan ikon.Anda dapat membuat ini semua sangat sederhana hanya dengan menggunakan filter SVG yang direferensikan dari CSS. Anda hanya memerlukan satu feColorMatrix untuk melakukan pewarnaan ulang. Yang ini berubah menjadi kuning. Kolom kelima di feColorMatrix menyimpan nilai target RGB pada skala unit. (untuk kuning - itu 1,1,0)
sumber
hue-rotate
di browser akan menyenangkan ya.url
fungsi caniuse.com/#search=svg%20filterSaya perhatikan bahwa contoh perawatan melalui filter SVG tidak lengkap, saya menulis milik saya (yang berfungsi dengan sempurna): (lihat jawaban Michael Mullany) jadi inilah cara untuk mendapatkan warna apa pun yang Anda inginkan:
Tampilkan cuplikan kode
Berikut adalah solusi kedua, dengan menggunakan SVG Filter hanya di code => URL.createObjectURL
Tampilkan cuplikan kode
sumber
gunakan saja
The
fill
properti di CSS adalah untuk mengisi warna bentuk SVG. Thefill
properti dapat menerima nilai warna CSS.sumber
img
elemen oleh browser.Saya mulai dengan jawaban ini menggunakan filter svg dan membuat modifikasi berikut:
Filter SVG dari url data
Jika Anda tidak ingin menentukan filter SVG di suatu tempat di markup Anda, Anda dapat menggunakan url data (ganti R , G , B dan A dengan warna yang diinginkan):
Pengganti grayscale
Jika versi di atas tidak berfungsi, Anda juga dapat menambahkan fallback grayscale.
Fungsi
saturate
danbrightness
mengubah warna apa pun menjadi hitam (Anda tidak perlu memasukkannya jika warnanya sudah hitam),invert
lalu mencerahkannya dengan kecerahan yang diinginkan ( L ) dan secara opsional Anda juga dapat menentukan opasitas ( A ).Campuran SCSS
Jika Anda ingin menentukan warna secara dinamis, Anda dapat menggunakan mixin SCSS berikut:
Contoh penggunaan:
Keuntungan:
hue-rotate
.Peringatan:
sumber