cahilfoley / react-snowfall

A high performance, canvas based react component that creates a snowfall effect
https://cahilfoley.github.io/react-snowfall/
MIT License
390 stars 22 forks source link

Loading images from CDN doesn't show the images #59

Closed netgfx closed 3 weeks ago

netgfx commented 1 year ago

Using image urls from cdn like https://phljyzcntikuxhwretud.supabase.co/storage/v1/object/public/assets/flake1.png

but it doesn't seem to work, no images appear or the default circles.

I'm creating the document images inside useEffect and changing a state variable with the array values. Is this acceptable or does it need some other way? The example on the docs is not very "React-y"

cahilfoley commented 1 year ago

I'll have to give this a test and have a look, but I suspect it's probably because the image hasn't been loaded by the time it's rendered to the offscreen canvas. This only happens once as an optimization so that might explain it.

Rather than creating the image elements in a useEffect I would recommend creating them with React and passing in a ref then using the ref to pass them into the config. I'll see if I can put together an example.

cahilfoley commented 3 weeks ago

Hey, sorry I know this is probably the slowest reply of all time but the issue with this was that the image data needed to be available when it's provided to the Snowfall component, here's how you could do it in a more "React-y" way.

const MyComponent = () => {
  const [images, setImages] = useState<HTMLImageElement[]>()

  useEffect(() => {
    let isMounted = true
    let image: HTMLImageElement | null = null
    let objectURL: string | null = null

    async function loadImageToState() {
      const imageData = await fetch(
        'https://phljyzcntikuxhwretud.supabase.co/storage/v1/object/public/assets/flake1.png'
      )
      const blob = await imageData.blob()

      if (!isMounted) return

      image = new Image()
      objectURL = URL.createObjectURL(blob)
      image.src = objectURL

      image.onload = () => {
        if (!isMounted) return
        setImages([image!])
      }
    }

    loadImageToState()

    return () => {
      isMounted = false
      if (image) image.remove()
      if (objectURL) URL.revokeObjectURL(objectURL)
      // Could be improved by using an abort controller to cancel the request
    }
  }, [])

  return <Snowfall images={images} />
}

Here's an example of the sandbox using the same technique to load the images https://codesandbox.io/p/devbox/snowfall-custom-cdn-images-v5rrhz. Hope this helps!

netgfx commented 3 weeks ago

Thanks for the explanation, I don't even remember why I needed that, but good to know. cheers!