概要
表題のことをしたかったが、あまり情報がなかった。
一旦renderToStaticMarkup
でdata-urlに変換することで表示できたので、メモ。
もっとスマートな方法があれば知りたいです。
環境
- "react": "^18.2.0"
- "react-icons": "^4.4.0"
- "react-konva": "^18.2.1"
- "konva": "^8.3.10"
ソース
import React from 'react'
export const useImage = (
url: string,
crossOrigin?: 'anonymous' | 'use-credentials',
) => {
const statusRef = React.useRef<'loading' | 'loaded' | 'failed'>('loading')
const imageRef = React.useRef<HTMLImageElement>()
const [_, setStateToken] = React.useState(0)
const oldUrl = React.useRef<string>()
const oldCrossOrigin = React.useRef<string>()
if (oldUrl.current !== url || oldCrossOrigin.current !== crossOrigin) {
statusRef.current = 'loading'
imageRef.current = undefined
oldUrl.current = url
oldCrossOrigin.current = crossOrigin
}
React.useLayoutEffect(
function () {
if (!url) return
const img = document.createElement('img')
function onload() {
statusRef.current = 'loaded'
imageRef.current = img
setStateToken(Math.random())
}
function onerror() {
statusRef.current = 'failed'
imageRef.current = undefined
setStateToken(Math.random())
}
img.addEventListener('load', onload)
img.addEventListener('error', onerror)
crossOrigin && (img.crossOrigin = crossOrigin)
img.src = url
return function cleanup() {
img.removeEventListener('load', onload)
img.removeEventListener('error', onerror)
}
},
[url, crossOrigin],
)
return [imageRef.current, statusRef.current] as const
}
icons.tsx
export { FaTag } from 'react-icons/fa'
import { renderToStaticMarkup } from 'react-dom/server'
import * as icons from '@/components/atoms/icon/icons'
import { useImage } from './useImage'
type Icons = keyof typeof icons
export const useIconImage = (icon: Icons, size?: number) => {
const svgString = encodeURIComponent(
renderToStaticMarkup(icons[icon]({ size })),
)
const dataUri = `data:image/svg+xml,${svgString}`
return useImage(dataUri)
}
const WithIconImage: React.FC = ({ children }) => {
const [image] = useIconImage('FaTag', 50)
return (
<Stage width={canvasWidth} height={canvasHight}>
<Layer>
<Image x={leftGap} y={pictTop} image={image} size={50} />
</Layer>
</Stage>
)
}