SVGカラー部分の処理
想定される用途は次のようなものです。
<Icon name="account" color="red" size={
[50,50]}/>
或者 size={
50} 这样的写法
したがって、次の 3 つのパラメータが必要です。
- 名前
- 色
- サイズ
前の記事の手順を実行すると、名前の要件が実現されたことがわかります。
size の実装は比較的単純なので、あまり詳しくは説明しません (size の処理プロセスは次のコードにも反映されています)、属性を svg に直接追加するだけです。簡単な例を見てみましょう。
return (
<svg
width={
200} // 把这里换成变量就可以了,或者将样式统一放在一个对象里,然后用展开符也行
height={
200}
aria-hidden="true"
>
<use xlinkHref={
iconName} />
</svg>
)
色の処理に関しては、2 つの異なる要件があります:
1 つは、元の SVG に付属する色を直接削除すること、およびすべての色はユーザーによってリセットされる (またはデフォルトの色を持つ) こと、もう 1 つはユーザー
がcolor 属性を渡しました 使用する場合はカスタム カラーのみを使用し、それ以外の場合は元の SVG カラーを維持します (これは私の要件でもあります)
これら 2 つの異なる要件では、処理スキームが異なります (svg-sprite-loader が使用される場合のみ)。最初の要件は実装が比較的簡単ですが、2 番目の要件はより面倒ですが、これについては後で別途説明します (場合によっては、私には実践がないので、アイデアや参考文献のみを示します)。
(1) 元のSVGカラーを削除する
いくつかのアイデアがあります:
1. 元の SVG 画像の fill のカラー値を直接削除する;
2. CSS を介して currentColor を設定して、元の色を上書きする;
元の塗りつぶしのカラー値を直接削除します
一部のプラグイン (svgo-loader など) を使用することも、スクリプトを自分で作成することもできます。
svgo-loaderをインストールする
GitHub:svgo-loader
SVGO は、SVG-as-XML データを SVG-as-JS AST 表現に変換します。次に、すべての AST データ項目に対して実行され、いくつかの操作が実行され、最後に SVGO は AST を SVG-as-XML データ文字列に変換します。
SVGO は、多くのプラグインを備えた SVG オプティマイザーです。SVG 要素の削除と変更、コンテンツの折りたたみ、属性の移動などを行うことができます。-------- 「svg-sprite-loaderを使ってアイコンを最適化する」
より抜粋
主なことは、設定ファイルにwebpack.config.js
自動削除を追加することですfill
。具体的なコードは次のとおりです ( Nuggets 作者の Moonwangerより)。
{
test: /\.svg$/,
use: [
{
loader: 'svg-sprite-loader', options: {
} },
{
loader: 'svgo-loader', options: {
plugins:[
// 加载时删除svg默认fill填充色
{
removeAttrs:{
attrs: 'fill'}}
]
}}
]
},
CSS 経由で currentColor を設定する
新しいスタイル ファイル (icon.less など) を作成して次の CSS を記述し、このスタイル ファイルを以前に作成した汎用コンポーネント icon.js にインポートするだけです。icon.js では、color
属性を svg タグに追加するだけです。
g[fill] {
fill: currentColor;
fill-opacity: 1;
}
g[stroke] {
stroke: currentColor;
stroke-opacity: 1;
}
path[fill] {
fill: currentColor;
fill-opacity: 1;
}
path[stroke] {
stroke: currentColor;
stroke-opacity: 1;
}
原理は非常にシンプルで、 svgファイル内のg
タグやタグpath
の属性を変更して色を制御するというものです。
(2) 元のSVGカラーを維持する
いくつかのアイデアがあります:
1. Web サイトの異なるテーマを切り替える実装と同様に、異なる CSS ファイルを動的に導入します (ただし、これは成功しませんでした) 2.
js セレクターを使用して、ID に従って対応するタグを選択します、ユーザーが color 属性をラベルに追加するとき。
2 番目の方法は、いくつかの方法を試して失敗した後、最終的に採用した方法ですが、アイデアは非常に単純で、svg-sprite-loader を使用した後に HTML ページを分析すると、各 svg 画像に対応するシンボル ID が異なることがわかります。 . ; シンボル タグの子は SVG イメージのパスであり、fill
属性はタグの色を制御します。このようにして、 を使用してdocument.getElementById()
このsymbol
タグを選択し、 を使用してchildren
その子要素を取得し、fill
パス内の属性を制御できます。このように、ユーザーが色を渡さない場合、
私のプロジェクトのディレクトリ構造は次のようになります。
- icons
- svg 这个文件夹用来放所有的svg图片
- src
- app.js
アイコン.js:
import React, {
useMemo, useState, useEffect } from 'react'
const Icon = ({
name,size,color}) => {
console.log(name,size,color)
const [svgModule, setSvgModule] = useState();
const [svgSize, setSvgSize] = useState({
width: 30,
height:30
});
// 允许自定义颜色
const setColor = () => {
let elem = document.getElementById(`${
name}`)
if(elem) {
let children = document.getElementById(`${
name}`).children
for(let i=0;i<children.length;i++) {
// foreach报错
children[i].style = `fill: ${
color}`; // 这里不能用with语句,严格模式不支持with
}
}
}
// 允许自定义尺寸
const setSize = () => {
if(!size){
setSvgSize({
width:30,height:30})
return
}
typeof size === "number" || "string" ?
setSvgSize({
width:size,height:size}) :
(size.length && size.length === 1 ?
setSvgSize({
width:size[0],height:size[0]}):
setSvgSize({
width:size[0],height:size[1]})
)
}
// 根据name拿到svg路径
const getSvg = async () => {
console.log("getSvg")
const svg = await import(`../../icons/svg/${
name}.svg`)
setSvgModule(svg)
}
const iconName = useMemo(() => {
setColor() // 保证页面刷新时不会因为找不到id为name的标签而报错
if (svgModule && svgModule.default) {
return `#${
svgModule.default.id}`
}
}, [svgModule])
useMemo(() => {
setSize()
}, [size])
useMemo( ()=>{
setColor()
},[color])
useEffect(() => {
getSvg()
}, [])
return (
<svg
{
...svgSize}
aria-hidden="true"
>
<use xlinkHref={
iconName} />
</svg>
)
}
export default Icon
(app.js) を使用するときに色を渡す効果:
import React from 'react';
import Icon from "./components/icons"
function App() {
return (
<div>
<p>test2</p>
<Icon name="account" size="200" color="pink"/>
<Icon name="pwd" color="green"/>
</div>
);
}
export default App;
カラーを渡さないことによる影響:
<div>
<p>test2</p>
<Icon name="account" size="200"/>
<Icon name="pwd"/>
</div>
適応
テストに使用した svg 画像は基本的に iconfont からダウンロードしたもので、構造は比較的均一です。たとえば、account.svg 画像の構造にはパスのラベルのみが含まれています。
ただし、プロジェクトでは、デザイナーが提供する一部の SVG 画像にさらに多くのレイヤーとタグがあり (たとえば、rect や g などの複数のタグがあります)、color 属性ではどのタグの塗りつぶしが含まれているかがわかりません。画像の色は上記のように svg を変更できない可能性が高いため、各ラベルをトラバースするのが最善であるため、各レイヤーが確実にトラバースされるように次の処理が行われます。
const setChildColor = (elem) => {
const {
children} = elem
if(children) {
for(let i=0;i<children.length;i++) {
children[i].style = `fill:${
color}`
if(children[i].children) {
setChildColor(children[i])
}
}
}
}
// 允许自定义颜色
const setColor = () => {
let elem = document.getElementById(`${
name}`)
if(elem) {
setChildColor(elem)
}
}