Hello guys, anyone have an idea of a more efficient way to load this balloon into the 3d world. In the code below you can see how I am loading it. The texture created is being applied to a sprite. During the initial load, my screen goes black and messes with my fucntionality. When i disable this svg loading, it works correctly. I cannot load them as pngs because the state of the ballon is determined dynamically based on the current state. The balloon has 3 modes which determine color, three types which determine the central part, and if its part of a route I need to number them correctly
export const useSVGTexture = (
svgString: string,
width: number,
height: number,
isURI = false
) => {
const [texture, setTexture] = useState<CanvasTexture | null>(null);
const imgLoader = useLoader(TextureLoader, svgString);
useEffect(() => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) return;
const img = new Image();
img.src = isURI ? svgString : imgLoader.image.currentSrc;
const imgLoaded = new Promise<void>((resolve) => {
if (img.complete) {
resolve();
} else {
img.onload = () => resolve();
}
});
imgLoaded.then(() => {
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
const canvasTexture = new CanvasTexture(canvas);
setTexture(canvasTexture);
// Cleanup: might not be necessary to manually remove the canvas or image in a React environment,
// as garbage collection should handle it once the components unmount.
// However, if you create a lot of textures or have memory concerns, you might consider additional cleanup strategies.
});
}, [svgString, width, height, imgLoader.image.currentSrc, isURI]);
return texture;
};
const Map3DSpriteBalloonTopComponent = (
props: Map3DSpriteBalloonTopProps
): JSX.Element | null => {
const { type, mode, position, dimmed, routeNumber } = props;
let circleColor;
let svgName;
switch (type) {
case Map3DBalloonSpriteType.HIVE:
svgName = 'mapSpriteBalloonHive';
break;
case Map3DBalloonSpriteType.LOCATION:
svgName = 'mapSpriteBalloonLocation';
break;
case Map3DBalloonSpriteType.ROUTE:
svgName = 'mapSpriteBalloonRoute';
break;
default:
svgName = 'mapSpriteBalloonRoute';
break;
}
switch (mode) {
case Map3DBalloonSpriteMode.EDIT:
circleColor = '#21A479';
break;
case Map3DBalloonSpriteMode.ERROR:
circleColor = '#FD9022';
break;
case Map3DBalloonSpriteMode.VIEW:
circleColor = '#4D8FFA';
break;
default:
circleColor = '#4D8FFA';
break;
}
let svgString = svgs[svgName].replace('CIRCLE_COLOR', circleColor);
if (type === Map3DBalloonSpriteType.ROUTE && routeNumber) {
svgString = svgString.replace('CIRCLE_NUMBER', String(routeNumber));
}
const svgUri = toSvgDataUri(svgString);
const texture = useSVGTexture(svgUri, 500, 500, true);
if (!texture) return null;
return (
<sprite
position={position}
scale={[1.5, 2.15, 1.5]}
center={new Vector2(0.5, 0.05)} // change the rotation center to be at the bottom instead of the middle of the sprite
>
<spriteMaterial opacity={dimmed ? 0.6 : 1} map={texture} />
</sprite>
);
};