vugu / vugu

Vugu: A modern UI library for Go+WebAssembly (experimental)
https://www.vugu.org
MIT License
4.8k stars 175 forks source link

Injecting an image into a Vugu component #227

Closed ebudan closed 2 years ago

ebudan commented 2 years ago

Hullo

Is there a neat way of injecting byte data into an img element inside a Vugu component?

I have a Vugu front end that communicates with the back end over WebSockets. One of the things it is receiving as server side push is an image (highly compressed JPEG byte data from a camera somewhere out there).

In theory, it shouldn't be too difficult: my Vugu component reads <img ... :src="c.ImgData" ...>, and my message handler encodes the incoming JPEG as c.ImgData = "data:image/jpeg;base64,..." with the properly encoded content.

Unfortunately Vugu's JSRenderer.instructionBuffer is limited at 16kB, and while I do try to squeeze the imagery quite a bit, the 10kB binaries splatted into base64 are going to be more than that. Vugu panics on refresh.

The other thing I tried doing was essentially:

    ...
    doc := js.Global().Get("document")
    img := doc.Call("getElementById", image_id)
    img.Set("src", image_data_b64)

...but here I'm probably poking my nose into Vugu's render cycle inappropriately, and I get no image. (The event cycle Lock/UnlockRender are taken care of, it's not just that.)

(Before anyone asks, the image isn't available over a URL. While I could provide that, what I really want to do next is to draw some extra image tracking results from other back end services arriving over WS, and I need to sync the image's known ID with the others. Providing the image and all its metadata over HTTP when all other communication is over WS feels sort of... inelegant.)

Any ideas on how to handle this within Go, Vugu, syscall/js? Might I be missing something simple with method #2 above?
Questions for elaboration absolutely welcome!

Dadido3 commented 2 years ago

I had the same problem, the solution i found was to create a blob on the JS side and fill it with the raw bytes of the compressed image (jpeg, png, ...). You can then derive an URL pointing to that blob:

https://github.com/Dadido3/D3surveyor/blob/03f4bcfbd2a601b2b223e86078721b6d5c990e02/camera-photo.go#L146-L151

Afterwards you can directly use that URL in your vugu HTML, or create a new JS image object to draw on a canvas or something similar. See:

<img vg-if='c.imageURL != ""' :src="c.imageURL" ...></img>

or

img := js.Global().Get("Image").New() // Create new JS image object.
img.Set("src", imageURL)

...

drawCtx.Call("drawImage", img, 0, 0)

I don't have a minimal example to show, but the few lines i linked/copied from my test project are hopefully explanatory enough.

Also, make sure to free the URL once you don't need the image anymore:

js.Global().Get("URL").Call("revokeObjectURL", imageURL)
ebudan commented 2 years ago

Dadido3 that worked perfectly. Thank you for taking the time to comment! (Also, your project looks interesting, I'll have to check it out.)