Webアプリでアイコンをたくさんつくる必要が生じた。 はじめは生成AIに頼んでつくっていたが、バリエーションが増えすぎたので ラベルと幅と色を指定して SVG アイコンを生成する React Component を書いた。
コードはこれ:
import React from 'react'
// @ts-ignore
import MyIcon, { MyBlueIcon, MyRedIcon, MyOrangeIcon, MyGreenIcon } from './MyIcon'
const App = (): React.ReactElement => {
return (
<>
<MyIcon
label="Hello, World!"
width={120}
fillColor="#1E90FF"
strokeColor="#0080FF"
/>
<br/>
<MyIcon
label="Hello"
width={84}
fillColor="#1E90FF"
strokeColor="#0080FF"
/>
<br/>
<MyIcon
label="OK"
width={48}
fillColor="#22C55E"
strokeColor="#16A34A"
/>
</>
)
}
export default App
ラベル文字列の長さに応じて適当なアイコンの幅を指定しています。
色を個別に指定していくが面倒なので、 MyBlueIcon, MyRedIcon, ... のように色だけあらかじめ指定してアイコンを用意しました。
コードはこれ:
import React from 'react'
// @ts-ignore
import MyIcon, { MyBlueIcon, MyRedIcon, MyOrangeIcon, MyGreenIcon } from './MyIcon'
const App = (): React.ReactElement => {
return (
<>
<MyBlueIcon label="Hello, World!" width={120} />
<br/>
<MyRedIcon label="Hello, World!" width={120} />
<br/>
<MyOrangeIcon label="Hello, World!" width={120} />
<br/>
<MyGreenIcon label="Hello, World!" width={120} />
</>
)
}
export default App
与えた文字列の長さに応じてその幅を自動計算できるとうれしいのですが。 React って JS を実行できたりするのかな。 ただ、そこまでやりはじめると、フォント名やフォントサイズの指定も必要。 アイコンの縦の大きさも計算したりとややこしい話になる。 特定の状況下で簡単に使いたいだけなので、このくらいでちょうどよい気がする。
最後に SVG を生成している本体になる MyIcon.tsx はこちら:
import React from 'react'
const style0: React.CSSProperties = {
stopColor: 'white',
stopOpacity: 0
}
const style1: React.CSSProperties = {
stopColor: 'white',
stopOpacity: 1
}
export const MyBlueIcon = (props: { label: string; width: number }): React.ReactElement => {
return (
<MyIcon
label={props.label}
width={props.width}
fillColor="#1E90FF"
strokeColor="#0080FF"
/>
)
}
export const MyRedIcon = (props: { label: string; width: number }): React.ReactElement => {
return (
<MyIcon
label={props.label}
width={props.width}
fillColor="#EF4444"
strokeColor="#DC2626"
/>
)
}
export const MyOrangeIcon = (props: { label: string; width: number }): React.ReactElement => {
return (
<MyIcon
label={props.label}
width={props.width}
fillColor="#FF8C00"
strokeColor="#E67E00"
/>
)
}
export const MyGreenIcon = (props: { label: string; width: number }): React.ReactElement => {
return (
<MyIcon
label={props.label}
width={props.width}
fillColor="#22C55E"
strokeColor="#16A34A"
/>
)
}
const MyIcon = (props: {
label: string
width: number
fillColor: string
strokeColor: string
}): React.ReactElement => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox={`0 0 ${props.width} 24`} width={`${props.width}`} height="24">
<rect x="1" y="1" width={`${props.width-2}`} height="22" rx="3" ry="3"
fill={props.fillColor} stroke={props.strokeColor} stroke-width="0.5"/>
<rect x="1" y="1" width={`${props.width-2}`} height="11" rx="3" ry="3"
fill="url(#myGradient)" opacity="0.3"/>
<text x={`${props.width/2}`} y="17" fontFamily="Arial, sans-serif" fontSize="14"
fontWeight="bold" textAnchor="middle" fill="white">{props.label}</text>
<defs>
<linearGradient id="myGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style={style1} />
<stop offset="100%" style={style0} />
</linearGradient>
</defs>
</svg>
)
}
export default MyIcon
MyIcon の色を指定済みのものとして MyBlueIcon, MyRedIcon, ... を定義しているだけ。 とてもわかりやすいけれど、それだけのことしかしていないのにこれだけのコードを書く必要があるというは冗長ではある。
AIに聞いてみたら解決方法あるんだろうね。たぶん。